pcm.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Module for interfacing with PCM functionality
  2. #include "module.h"
  3. #include "lauxlib.h"
  4. #include "task/task.h"
  5. #include "c_string.h"
  6. #include "c_stdlib.h"
  7. #include "pcm.h"
  8. #include "pcm_drv.h"
  9. #define GET_PUD() pud_t *pud = (pud_t *)luaL_checkudata(L, 1, "pcm.driver"); \
  10. cfg_t *cfg = &(pud->cfg);
  11. #define UNREF_CB(_cb) luaL_unref( L, LUA_REGISTRYINDEX, _cb ); \
  12. _cb = LUA_NOREF;
  13. #define COND_REF(_cond) _cond ? luaL_ref( L, LUA_REGISTRYINDEX ) : LUA_NOREF
  14. static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int returns )
  15. {
  16. if (cb_ref != LUA_NOREF) {
  17. lua_rawgeti( L, LUA_REGISTRYINDEX, cb_ref );
  18. lua_rawgeti( L, LUA_REGISTRYINDEX, self_ref );
  19. lua_call( L, 1, returns );
  20. }
  21. }
  22. static int pcm_drv_free( lua_State *L )
  23. {
  24. GET_PUD();
  25. UNREF_CB( cfg->cb_data_ref );
  26. UNREF_CB( cfg->cb_drained_ref );
  27. UNREF_CB( cfg->cb_paused_ref );
  28. UNREF_CB( cfg->cb_stopped_ref );
  29. UNREF_CB( cfg->cb_vu_ref );
  30. UNREF_CB( cfg->self_ref );
  31. if (cfg->bufs[0].data) {
  32. c_free( cfg->bufs[0].data );
  33. cfg->bufs[0].data = NULL;
  34. }
  35. if (cfg->bufs[1].data) {
  36. c_free( cfg->bufs[1].data );
  37. cfg->bufs[1].data = NULL;
  38. }
  39. return 0;
  40. }
  41. // Lua: drv:close()
  42. static int pcm_drv_close( lua_State *L )
  43. {
  44. GET_PUD();
  45. pud->drv->close( cfg );
  46. return pcm_drv_free( L );
  47. }
  48. // Lua: drv:stop(self)
  49. static int pcm_drv_stop( lua_State *L )
  50. {
  51. GET_PUD();
  52. // throttle ISR and reader
  53. cfg->isr_throttled = -1;
  54. pud->drv->stop( cfg );
  55. // invalidate the buffers
  56. cfg->bufs[0].empty = cfg->bufs[1].empty = TRUE;
  57. dispatch_callback( L, cfg->self_ref, cfg->cb_stopped_ref, 0 );
  58. return 0;
  59. }
  60. // Lua: drv:pause(self)
  61. static int pcm_drv_pause( lua_State *L )
  62. {
  63. GET_PUD();
  64. // throttle ISR and reader
  65. cfg->isr_throttled = -1;
  66. pud->drv->stop( cfg );
  67. dispatch_callback( L, cfg->self_ref, cfg->cb_paused_ref, 0 );
  68. return 0;
  69. }
  70. static void pcm_start_play_task( task_param_t param, uint8 prio )
  71. {
  72. lua_State *L = lua_getstate();
  73. pud_t *pud = (pud_t *)param;
  74. cfg_t *cfg = &(pud->cfg);
  75. // stop driver before starting it in case it hasn't been stopped from Lua
  76. pud->drv->stop( cfg );
  77. if (!pud->drv->play( cfg )) {
  78. luaL_error( L, "pcm driver start" );
  79. }
  80. // unthrottle ISR and reader
  81. pud->cfg.isr_throttled = 0;
  82. }
  83. // Lua: drv:play(self, rate)
  84. static int pcm_drv_play( lua_State *L )
  85. {
  86. GET_PUD();
  87. cfg->rate = luaL_optinteger( L, 2, PCM_RATE_8K );
  88. luaL_argcheck( L, (cfg->rate >= PCM_RATE_1K) && (cfg->rate <= PCM_RATE_16K), 2, "invalid bit rate" );
  89. if (cfg->self_ref == LUA_NOREF) {
  90. lua_pushvalue( L, 1 ); // copy self userdata to the top of stack
  91. cfg->self_ref = luaL_ref( L, LUA_REGISTRYINDEX );
  92. }
  93. // schedule actions for play in separate task since drv:play() might have been called
  94. // in the callback fn of pcm_data_play_task() which in turn gets called when starting play...
  95. task_post_low( cfg->start_play_task, (os_param_t)pud );
  96. return 0;
  97. }
  98. // Lua: drv.on(self, event, cb_fn)
  99. static int pcm_drv_on( lua_State *L )
  100. {
  101. size_t len;
  102. const char *event;
  103. uint8_t is_func = FALSE;
  104. GET_PUD();
  105. event = luaL_checklstring( L, 2, &len );
  106. if ((lua_type( L, 3 ) == LUA_TFUNCTION) ||
  107. (lua_type( L, 3 ) == LUA_TLIGHTFUNCTION)) {
  108. lua_pushvalue( L, 3 ); // copy argument (func) to the top of stack
  109. is_func = TRUE;
  110. }
  111. if ((len == 4) && (c_strcmp( event, "data" ) == 0)) {
  112. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_data_ref);
  113. cfg->cb_data_ref = COND_REF( is_func );
  114. } else if ((len == 7) && (c_strcmp( event, "drained" ) == 0)) {
  115. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_drained_ref);
  116. cfg->cb_drained_ref = COND_REF( is_func );
  117. } else if ((len == 6) && (c_strcmp( event, "paused" ) == 0)) {
  118. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_paused_ref);
  119. cfg->cb_paused_ref = COND_REF( is_func );
  120. } else if ((len == 7) && (c_strcmp( event, "stopped" ) == 0)) {
  121. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_stopped_ref);
  122. cfg->cb_stopped_ref = COND_REF( is_func );
  123. } else if ((len == 2) && (c_strcmp( event, "vu" ) == 0)) {
  124. luaL_unref( L, LUA_REGISTRYINDEX, cfg->cb_vu_ref);
  125. cfg->cb_vu_ref = COND_REF( is_func );
  126. int freq = luaL_optinteger( L, 4, 10 );
  127. luaL_argcheck( L, (freq > 0) && (freq <= 200), 4, "invalid range" );
  128. cfg->vu_freq = (uint8_t)freq;
  129. } else {
  130. if (is_func) {
  131. // need to pop pushed function arg
  132. lua_pop( L, 1 );
  133. }
  134. return luaL_error( L, "method not supported" );
  135. }
  136. return 0;
  137. }
  138. // Lua: pcm.new( type, pin )
  139. static int pcm_new( lua_State *L )
  140. {
  141. pud_t *pud = (pud_t *) lua_newuserdata( L, sizeof( pud_t ) );
  142. cfg_t *cfg = &(pud->cfg);
  143. int driver;
  144. cfg->rbuf_idx = cfg->fbuf_idx = 0;
  145. cfg->isr_throttled = -1; // start ISR and reader in throttled mode
  146. driver = luaL_checkinteger( L, 1 );
  147. luaL_argcheck( L, (driver >= 0) && (driver < PCM_DRIVER_END), 1, "invalid driver" );
  148. cfg->self_ref = LUA_NOREF;
  149. cfg->cb_data_ref = cfg->cb_drained_ref = LUA_NOREF;
  150. cfg->cb_paused_ref = cfg->cb_stopped_ref = LUA_NOREF;
  151. cfg->cb_vu_ref = LUA_NOREF;
  152. cfg->bufs[0].buf_size = cfg->bufs[1].buf_size = 0;
  153. cfg->bufs[0].data = cfg->bufs[1].data = NULL;
  154. cfg->bufs[0].len = cfg->bufs[1].len = 0;
  155. cfg->bufs[0].rpos = cfg->bufs[1].rpos = 0;
  156. cfg->bufs[0].empty = cfg->bufs[1].empty = TRUE;
  157. cfg->data_vu_task = task_get_id( pcm_data_vu_task );
  158. cfg->vu_freq = 10;
  159. cfg->data_play_task = task_get_id( pcm_data_play_task );
  160. cfg->start_play_task = task_get_id( pcm_start_play_task );
  161. if (driver == PCM_DRIVER_SD) {
  162. cfg->pin = luaL_checkinteger( L, 2 );
  163. MOD_CHECK_ID(sigma_delta, cfg->pin);
  164. pud->drv = &pcm_drv_sd;
  165. pud->drv->init( cfg );
  166. /* set its metatable */
  167. lua_pushvalue( L, -1 ); // copy self userdata to the top of stack
  168. luaL_getmetatable( L, "pcm.driver" );
  169. lua_setmetatable( L, -2 );
  170. return 1;
  171. } else {
  172. pud->drv = NULL;
  173. return 0;
  174. }
  175. }
  176. static const LUA_REG_TYPE pcm_driver_map[] = {
  177. { LSTRKEY( "play" ), LFUNCVAL( pcm_drv_play ) },
  178. { LSTRKEY( "pause" ), LFUNCVAL( pcm_drv_pause ) },
  179. { LSTRKEY( "stop" ), LFUNCVAL( pcm_drv_stop ) },
  180. { LSTRKEY( "close" ), LFUNCVAL( pcm_drv_close ) },
  181. { LSTRKEY( "on" ), LFUNCVAL( pcm_drv_on ) },
  182. { LSTRKEY( "__gc" ), LFUNCVAL( pcm_drv_free ) },
  183. { LSTRKEY( "__index" ), LROVAL( pcm_driver_map ) },
  184. { LNILKEY, LNILVAL }
  185. };
  186. // Module function map
  187. static const LUA_REG_TYPE pcm_map[] = {
  188. { LSTRKEY( "new" ), LFUNCVAL( pcm_new ) },
  189. { LSTRKEY( "SD" ), LNUMVAL( PCM_DRIVER_SD ) },
  190. { LSTRKEY( "RATE_1K" ), LNUMVAL( PCM_RATE_1K ) },
  191. { LSTRKEY( "RATE_2K" ), LNUMVAL( PCM_RATE_2K ) },
  192. { LSTRKEY( "RATE_4K" ), LNUMVAL( PCM_RATE_4K ) },
  193. { LSTRKEY( "RATE_5K" ), LNUMVAL( PCM_RATE_5K ) },
  194. { LSTRKEY( "RATE_8K" ), LNUMVAL( PCM_RATE_8K ) },
  195. { LSTRKEY( "RATE_10K" ), LNUMVAL( PCM_RATE_10K ) },
  196. { LSTRKEY( "RATE_12K" ), LNUMVAL( PCM_RATE_12K ) },
  197. { LSTRKEY( "RATE_16K" ), LNUMVAL( PCM_RATE_16K ) },
  198. { LNILKEY, LNILVAL }
  199. };
  200. int luaopen_pcm( lua_State *L ) {
  201. luaL_rometatable( L, "pcm.driver", (void *)pcm_driver_map ); // create metatable
  202. return 0;
  203. }
  204. NODEMCU_MODULE(PCM, "pcm", pcm_map, luaopen_pcm);