@@ -1336,6 +1336,11 @@ impl PyAudioEngineBackend {
13361336 Ok ( out)
13371337 }
13381338
1339+ fn assert_routes_available ( & self , route_ids : Vec < String > ) -> PyResult < ( ) > {
1340+ self . ensure_open ( ) ?;
1341+ self . ensure_route_consumers_available ( & route_ids)
1342+ }
1343+
13391344 fn close ( & mut self , py : Python < ' _ > ) -> PyResult < ( ) > {
13401345 // `closed` means no new public operations are allowed. If native cleanup timed out earlier,
13411346 // repeated close attempts are still allowed so drop/explicit close can keep retrying the
@@ -1469,6 +1474,44 @@ impl PyAudioEngineBackend {
14691474 Ok ( ( ) )
14701475 }
14711476
1477+ fn start_stream_states_for_routes (
1478+ & mut self ,
1479+ py : Python < ' _ > ,
1480+ route_ids : & [ String ] ,
1481+ ) -> PyResult < ( ) > {
1482+ let stream_ids = self . stream_ids_for_routes ( route_ids) ?;
1483+ let stream_states = self . take_stream_states ( & stream_ids) ?;
1484+ let started_states = match py. detach ( move || {
1485+ start_stream_states_with_timeout ( stream_states, NATIVE_SOURCE_START_TIMEOUT )
1486+ } ) {
1487+ Ok ( states) => states,
1488+ Err ( StartStreamsError {
1489+ message,
1490+ states,
1491+ poison,
1492+ cleanup,
1493+ } ) => {
1494+ self . restore_stream_states ( states) ;
1495+ self . store_pending_cleanups ( cleanup) ;
1496+ self . poisoned = poison;
1497+ return Err ( PyRuntimeError :: new_err ( message) ) ;
1498+ }
1499+ } ;
1500+ self . restore_stream_states ( started_states) ;
1501+ Ok ( ( ) )
1502+ }
1503+
1504+ fn prepare_route_consumers_for_sink (
1505+ & mut self ,
1506+ py : Python < ' _ > ,
1507+ route_ids : & [ String ] ,
1508+ ) -> PyResult < Vec < ( String , RouteConsumer ) > > {
1509+ self . ensure_open ( ) ?;
1510+ self . ensure_route_consumers_available ( route_ids) ?;
1511+ self . start_stream_states_for_routes ( py, route_ids) ?;
1512+ self . take_route_consumers ( route_ids)
1513+ }
1514+
14721515 fn stream_ids_for_routes ( & self , route_ids : & [ String ] ) -> PyResult < Vec < String > > {
14731516 let mut stream_ids = Vec :: < String > :: new ( ) ;
14741517
@@ -1554,9 +1597,6 @@ impl PyAudioEngineBackend {
15541597 chunk_frames : usize ,
15551598 callback : Py < PyAny > ,
15561599 ) -> PyResult < PyAsrSinkBackend > {
1557- self . ensure_open ( ) ?;
1558- self . ensure_route_consumers_available ( & route_ids) ?;
1559-
15601600 let format = StreamFormat :: with_sample_format (
15611601 sample_rate,
15621602 channels,
@@ -1568,27 +1608,7 @@ impl PyAudioEngineBackend {
15681608 } )
15691609 . map_err ( |err| PyValueError :: new_err ( err. to_string ( ) ) ) ?;
15701610
1571- let stream_ids = self . stream_ids_for_routes ( & route_ids) ?;
1572- let stream_states = self . take_stream_states ( & stream_ids) ?;
1573- let started_states = match py. detach ( move || {
1574- start_stream_states_with_timeout ( stream_states, NATIVE_SOURCE_START_TIMEOUT )
1575- } ) {
1576- Ok ( states) => states,
1577- Err ( StartStreamsError {
1578- message,
1579- states,
1580- poison,
1581- cleanup,
1582- } ) => {
1583- self . restore_stream_states ( states) ;
1584- self . store_pending_cleanups ( cleanup) ;
1585- self . poisoned = poison;
1586- return Err ( PyRuntimeError :: new_err ( message) ) ;
1587- }
1588- } ;
1589- self . restore_stream_states ( started_states) ;
1590-
1591- let route_consumers = self . take_route_consumers ( & route_ids) ?;
1611+ let route_consumers = self . prepare_route_consumers_for_sink ( py, & route_ids) ?;
15921612 let detached_result: DetachedAsrStartResult = py. detach ( move || {
15931613 let inputs = route_consumers
15941614 . into_iter ( )
@@ -1640,32 +1660,9 @@ impl PyAudioEngineBackend {
16401660 fd : i32 ,
16411661 mix_gain : f32 ,
16421662 ) -> PyResult < PyWavSinkBackend > {
1643- self . ensure_open ( ) ?;
1644- self . ensure_route_consumers_available ( & route_ids) ?;
1645-
16461663 let file = duplicate_file_descriptor ( fd)
16471664 . map_err ( |e| PyOSError :: new_err ( format ! ( "failed to duplicate file descriptor: {e}" ) ) ) ?;
1648- let stream_ids = self . stream_ids_for_routes ( & route_ids) ?;
1649- let stream_states = self . take_stream_states ( & stream_ids) ?;
1650- let started_states = match py. detach ( move || {
1651- start_stream_states_with_timeout ( stream_states, NATIVE_SOURCE_START_TIMEOUT )
1652- } ) {
1653- Ok ( states) => states,
1654- Err ( StartStreamsError {
1655- message,
1656- states,
1657- poison,
1658- cleanup,
1659- } ) => {
1660- self . restore_stream_states ( states) ;
1661- self . store_pending_cleanups ( cleanup) ;
1662- self . poisoned = poison;
1663- return Err ( PyRuntimeError :: new_err ( message) ) ;
1664- }
1665- } ;
1666- self . restore_stream_states ( started_states) ;
1667-
1668- let route_consumers = self . take_route_consumers ( & route_ids) ?;
1665+ let route_consumers = self . prepare_route_consumers_for_sink ( py, & route_ids) ?;
16691666 let route_ids_for_sink = route_ids. clone ( ) ;
16701667 let master_format = self . controller . master_format ( ) ;
16711668 let detached_result: DetachedWavStartResult = py. detach ( move || {
0 commit comments