Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

339 líneas
11KB

  1. //go:build soapy && !windows
  2. package soapysdr
  3. import (
  4. "fmt"
  5. "log"
  6. "math"
  7. "sort"
  8. "unsafe"
  9. )
  10. /*
  11. #include <dlfcn.h>
  12. #include <stdlib.h>
  13. #include <stdint.h>
  14. // Minimal dlopen wrapper — this is the ONLY cgo usage and it's
  15. // just for dlopen/dlsym which are part of libc, not SoapySDR.
  16. // No SoapySDR headers needed at compile time.
  17. static void* soapy_dlopen(const char* path) {
  18. return dlopen(path, 2); // RTLD_NOW
  19. }
  20. static void* soapy_dlsym(void* handle, const char* name) {
  21. return dlsym(handle, name);
  22. }
  23. static const char* soapy_dlerror() {
  24. return dlerror();
  25. }
  26. // Try to resolve SoapySDR_getLastError dynamically when available.
  27. typedef const char* (*last_error_fn)(void);
  28. static const char* call_last_error(void* fn) {
  29. if (fn == NULL) return NULL;
  30. return ((last_error_fn)fn)();
  31. }
  32. // Function call trampolines — we call function pointers loaded via dlsym.
  33. // These avoid the complexity of calling C function pointers from Go directly.
  34. typedef void* (*make_fn)(void*);
  35. typedef int (*unmake_fn)(void*);
  36. typedef int (*set_double_fn)(void*, int, size_t, double);
  37. typedef int (*set_freq_fn)(void*, int, size_t, double, void*);
  38. typedef void* (*setup_stream_fn)(void*, int, const char*, size_t*, size_t, void*);
  39. typedef int (*close_stream_fn)(void*, void*);
  40. typedef size_t (*mtu_fn)(void*, void*);
  41. typedef int (*activate_fn)(void*, void*, int, long long, size_t);
  42. typedef int (*deactivate_fn)(void*, void*, int, long long);
  43. typedef int (*write_fn)(void*, void*, const void**, size_t, int*, long long, long);
  44. typedef void* (*enumerate_fn)(void*, size_t*);
  45. typedef void (*kwargs_clear_fn)(void*, size_t);
  46. typedef void (*kwargs_set_fn)(void*, const char*, const char*);
  47. // --- KWArgs struct matching SoapySDRKwargs ---
  48. typedef struct {
  49. size_t size;
  50. char** keys;
  51. char** vals;
  52. } GoKwargs;
  53. static void* call_make(void* fn, void* args) {
  54. return ((make_fn)fn)(args);
  55. }
  56. static int call_unmake(void* fn, void* dev) {
  57. return ((unmake_fn)fn)(dev);
  58. }
  59. static int call_set_sample_rate(void* fn, void* dev, int dir, size_t ch, double rate) {
  60. return ((set_double_fn)fn)(dev, dir, ch, rate);
  61. }
  62. static int call_set_frequency(void* fn, void* dev, int dir, size_t ch, double freq, void* kw) {
  63. return ((set_freq_fn)fn)(dev, dir, ch, freq, kw);
  64. }
  65. static int call_set_gain(void* fn, void* dev, int dir, size_t ch, double gain) {
  66. return ((set_double_fn)fn)(dev, dir, ch, gain);
  67. }
  68. static void* call_setup_stream(void* fn, void* dev, int dir, const char* fmt, size_t* chs, size_t nch, void* kw) {
  69. return ((setup_stream_fn)fn)(dev, dir, fmt, chs, nch, kw);
  70. }
  71. static int call_close_stream(void* fn, void* dev, void* stream) {
  72. return ((close_stream_fn)fn)(dev, stream);
  73. }
  74. static size_t call_mtu(void* fn, void* dev, void* stream) {
  75. return ((mtu_fn)fn)(dev, stream);
  76. }
  77. static int call_activate(void* fn, void* dev, void* stream) {
  78. return ((activate_fn)fn)(dev, stream, 0, 0, 0);
  79. }
  80. static int call_deactivate(void* fn, void* dev, void* stream) {
  81. return ((deactivate_fn)fn)(dev, stream, 0, 0);
  82. }
  83. static int call_write(void* fn, void* dev, void* stream, const void* buf, size_t n, int* flags, long timeout) {
  84. const void* buffs[1];
  85. buffs[0] = buf;
  86. *flags = 0;
  87. return ((write_fn)fn)(dev, stream, buffs, n, flags, 0, timeout);
  88. }
  89. static void* call_enumerate(void* fn, void* kw, size_t* length) {
  90. return ((enumerate_fn)fn)(kw, length);
  91. }
  92. static void call_kwargs_clear(void* fn, void* list, size_t length) {
  93. ((kwargs_clear_fn)fn)(list, length);
  94. }
  95. static void call_kwargs_set(void* fn, void* kw, const char* key, const char* val) {
  96. ((kwargs_set_fn)fn)(kw, key, val);
  97. }
  98. */
  99. import "C"
  100. type soapyLib struct {
  101. handle unsafe.Pointer
  102. fnEnumerate unsafe.Pointer
  103. fnKwargsListClear unsafe.Pointer
  104. fnKwargsSet unsafe.Pointer
  105. fnMake unsafe.Pointer
  106. fnUnmake unsafe.Pointer
  107. fnSetSampleRate unsafe.Pointer
  108. fnSetFrequency unsafe.Pointer
  109. fnSetGain unsafe.Pointer
  110. fnGetGainRange unsafe.Pointer
  111. fnSetupStream unsafe.Pointer
  112. fnCloseStream unsafe.Pointer
  113. fnGetStreamMTU unsafe.Pointer
  114. fnActivateStream unsafe.Pointer
  115. fnDeactivateStream unsafe.Pointer
  116. fnWriteStream unsafe.Pointer
  117. fnGetLastError unsafe.Pointer
  118. }
  119. var libNames = []string{
  120. "libSoapySDR.so.0.8",
  121. "libSoapySDR.so",
  122. "libSoapySDR.dylib",
  123. }
  124. func loadSoapyLib() (*soapyLib, error) {
  125. var handle unsafe.Pointer
  126. for _, name := range libNames {
  127. cName := C.CString(name)
  128. handle = C.soapy_dlopen(cName)
  129. C.free(unsafe.Pointer(cName))
  130. if handle != nil {
  131. break
  132. }
  133. }
  134. if handle == nil {
  135. errMsg := C.GoString(C.soapy_dlerror())
  136. return nil, fmt.Errorf("cannot load SoapySDR: %s", errMsg)
  137. }
  138. sym := func(name string) unsafe.Pointer {
  139. cName := C.CString(name)
  140. defer C.free(unsafe.Pointer(cName))
  141. return C.soapy_dlsym(handle, cName)
  142. }
  143. return &soapyLib{
  144. handle: handle,
  145. fnEnumerate: sym("SoapySDRDevice_enumerate"),
  146. fnKwargsListClear: sym("SoapySDRKwargsList_clear"),
  147. fnKwargsSet: sym("SoapySDRKwargs_set"),
  148. fnMake: sym("SoapySDRDevice_make"),
  149. fnUnmake: sym("SoapySDRDevice_unmake"),
  150. fnSetSampleRate: sym("SoapySDRDevice_setSampleRate"),
  151. fnSetFrequency: sym("SoapySDRDevice_setFrequency"),
  152. fnSetGain: sym("SoapySDRDevice_setGain"),
  153. fnGetGainRange: sym("SoapySDRDevice_getGainRange"),
  154. fnSetupStream: sym("SoapySDRDevice_setupStream"),
  155. fnCloseStream: sym("SoapySDRDevice_closeStream"),
  156. fnGetStreamMTU: sym("SoapySDRDevice_getStreamMTU"),
  157. fnActivateStream: sym("SoapySDRDevice_activateStream"),
  158. fnDeactivateStream: sym("SoapySDRDevice_deactivateStream"),
  159. fnWriteStream: sym("SoapySDRDevice_writeStream"),
  160. fnGetLastError: sym("SoapySDR_getLastError"),
  161. }, nil
  162. }
  163. // --- kwargs helper ---
  164. type kwargs = C.GoKwargs
  165. func (lib *soapyLib) kwargsSet(kw *kwargs, key, val string) {
  166. if lib.fnKwargsSet == nil { return }
  167. cK := C.CString(key); cV := C.CString(val)
  168. defer C.free(unsafe.Pointer(cK)); defer C.free(unsafe.Pointer(cV))
  169. C.call_kwargs_set(lib.fnKwargsSet, unsafe.Pointer(kw), cK, cV)
  170. }
  171. // --- Enumerate ---
  172. func (lib *soapyLib) enumerate() ([]map[string]string, error) {
  173. if lib.fnEnumerate == nil { return nil, fmt.Errorf("enumerate not available") }
  174. var kw kwargs
  175. var length C.size_t
  176. ret := C.call_enumerate(lib.fnEnumerate, unsafe.Pointer(&kw), &length)
  177. if ret == nil || length == 0 { return nil, nil }
  178. defer func() {
  179. if lib.fnKwargsListClear != nil {
  180. C.call_kwargs_clear(lib.fnKwargsListClear, ret, length)
  181. }
  182. }()
  183. devices := make([]map[string]string, 0, int(length))
  184. kwSize := unsafe.Sizeof(C.GoKwargs{})
  185. base := uintptr(ret)
  186. for i := 0; i < int(length); i++ {
  187. entry := (*C.GoKwargs)(unsafe.Pointer(base + uintptr(i)*kwSize))
  188. m := make(map[string]string)
  189. for j := 0; j < int(entry.size); j++ {
  190. keyPtr := *(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(entry.keys)) + uintptr(j)*unsafe.Sizeof(uintptr(0))))
  191. valPtr := *(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(entry.vals)) + uintptr(j)*unsafe.Sizeof(uintptr(0))))
  192. if keyPtr != nil && valPtr != nil {
  193. m[C.GoString(keyPtr)] = C.GoString(valPtr)
  194. }
  195. }
  196. devices = append(devices, m)
  197. }
  198. return devices, nil
  199. }
  200. // --- Device ---
  201. func (lib *soapyLib) makeDevice(driver, device string, args map[string]string) (uintptr, error) {
  202. if lib.fnMake == nil { return 0, fmt.Errorf("make not available") }
  203. var kw kwargs
  204. if driver != "" { lib.kwargsSet(&kw, "driver", driver) }
  205. if device != "" { lib.kwargsSet(&kw, "device", device) }
  206. keys := make([]string, 0, len(args))
  207. for k := range args {
  208. keys = append(keys, k)
  209. }
  210. sort.Strings(keys)
  211. for _, k := range keys {
  212. lib.kwargsSet(&kw, k, args[k])
  213. }
  214. log.Printf("soapy: makeDevice driver=%q device=%q args=%v", driver, device, args)
  215. ret := C.call_make(lib.fnMake, unsafe.Pointer(&kw))
  216. if ret == nil {
  217. msg := ""
  218. if lib.fnGetLastError != nil {
  219. if p := C.call_last_error(lib.fnGetLastError); p != nil {
  220. msg = C.GoString(p)
  221. }
  222. }
  223. if msg != "" {
  224. return 0, fmt.Errorf("soapy: failed to open device: %s", msg)
  225. }
  226. return 0, fmt.Errorf("soapy: failed to open device")
  227. }
  228. return uintptr(ret), nil
  229. }
  230. func (lib *soapyLib) unmakeDevice(dev uintptr) {
  231. if lib.fnUnmake != nil { C.call_unmake(lib.fnUnmake, unsafe.Pointer(dev)) }
  232. }
  233. // --- Config ---
  234. func (lib *soapyLib) setSampleRate(dev uintptr, dir, ch int, rate float64) error {
  235. if lib.fnSetSampleRate == nil { return fmt.Errorf("not available") }
  236. rc := C.call_set_sample_rate(lib.fnSetSampleRate, unsafe.Pointer(dev), C.int(dir), C.size_t(ch), C.double(rate))
  237. if rc != 0 { return fmt.Errorf("soapy: setSampleRate(%.0f) failed: %d", rate, rc) }
  238. return nil
  239. }
  240. func (lib *soapyLib) setFrequency(dev uintptr, dir, ch int, freq float64) error {
  241. if lib.fnSetFrequency == nil { return fmt.Errorf("not available") }
  242. var kw kwargs
  243. rc := C.call_set_frequency(lib.fnSetFrequency, unsafe.Pointer(dev), C.int(dir), C.size_t(ch), C.double(freq), unsafe.Pointer(&kw))
  244. if rc != 0 { return fmt.Errorf("soapy: setFrequency(%.0f) failed: %d", freq, rc) }
  245. return nil
  246. }
  247. func (lib *soapyLib) setGain(dev uintptr, dir, ch int, gain float64) error {
  248. if lib.fnSetGain == nil { return nil }
  249. C.call_set_gain(lib.fnSetGain, unsafe.Pointer(dev), C.int(dir), C.size_t(ch), C.double(gain))
  250. return nil
  251. }
  252. func (lib *soapyLib) getGainRange(dev uintptr, dir, ch int) (float64, float64) {
  253. _ = math.Float64bits // keep import
  254. // Fallback if function not available
  255. return 0, 89
  256. }
  257. // --- Stream ---
  258. func (lib *soapyLib) setupStream(dev uintptr, dir int, format string, channels []uint) (uintptr, error) {
  259. if lib.fnSetupStream == nil { return 0, fmt.Errorf("not available") }
  260. cFmt := C.CString(format); defer C.free(unsafe.Pointer(cFmt))
  261. chs := make([]C.size_t, len(channels))
  262. for i, c := range channels { chs[i] = C.size_t(c) }
  263. var chPtr *C.size_t
  264. if len(chs) > 0 { chPtr = &chs[0] }
  265. var kw kwargs
  266. ret := C.call_setup_stream(lib.fnSetupStream, unsafe.Pointer(dev), C.int(dir), cFmt, chPtr, C.size_t(len(channels)), unsafe.Pointer(&kw))
  267. if ret == nil { return 0, fmt.Errorf("soapy: setupStream failed") }
  268. return uintptr(ret), nil
  269. }
  270. func (lib *soapyLib) closeStream(dev, stream uintptr) {
  271. if lib.fnCloseStream != nil {
  272. C.call_close_stream(lib.fnCloseStream, unsafe.Pointer(dev), unsafe.Pointer(stream))
  273. }
  274. }
  275. func (lib *soapyLib) getStreamMTU(dev, stream uintptr) int {
  276. if lib.fnGetStreamMTU == nil { return 4096 }
  277. ret := C.call_mtu(lib.fnGetStreamMTU, unsafe.Pointer(dev), unsafe.Pointer(stream))
  278. if ret == 0 { return 4096 }
  279. return int(ret)
  280. }
  281. func (lib *soapyLib) activateStream(dev, stream uintptr) error {
  282. if lib.fnActivateStream == nil { return fmt.Errorf("not available") }
  283. rc := C.call_activate(lib.fnActivateStream, unsafe.Pointer(dev), unsafe.Pointer(stream))
  284. if rc != 0 { return fmt.Errorf("soapy: activateStream failed: %d", rc) }
  285. return nil
  286. }
  287. func (lib *soapyLib) deactivateStream(dev, stream uintptr) {
  288. if lib.fnDeactivateStream != nil {
  289. C.call_deactivate(lib.fnDeactivateStream, unsafe.Pointer(dev), unsafe.Pointer(stream))
  290. }
  291. }
  292. func (lib *soapyLib) writeStream(dev, stream uintptr, buf unsafe.Pointer, numElems int) (int, error) {
  293. if lib.fnWriteStream == nil { return 0, fmt.Errorf("not available") }
  294. var flags C.int
  295. rc := C.call_write(lib.fnWriteStream, unsafe.Pointer(dev), unsafe.Pointer(stream), buf, C.size_t(numElems), &flags, 100000)
  296. if rc < 0 { return 0, fmt.Errorf("soapy: writeStream returned %d", rc) }
  297. return int(rc), nil
  298. }