#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) ,
This is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : / / www . gnu . org / licenses / > .
"""
import logging ; logger = logging . getLogger ( __name__ ) ; logger . info ( " import " )
#Standard Library
import atexit
import sys
#Third Party
import engine . jacklib as jacklib
from ctypes import pointer
#Our Modules
from engine . config import METADATA #includes METADATA only. No other environmental setup is executed.
from engine . jacklib . helpers import get_jack_status_error_string
class AgordejoJackClient ( object ) :
""" Singleton. Created in api.startEngine.
If client cannot be started the program will exit from here .
Most error sources are already ruled out in start . py . """
def __init__ ( self ) :
status = jacklib . jack_status_t ( )
self . _jacklibClient = jacklib . client_open ( METADATA [ " name " ] , jacklib . JackNoStartServer , status )
err = get_jack_status_error_string ( status )
if not status . value == 0 :
#Decide if a name collision is important or not. Agordejo cannot be started two times anyway. So this would be another client called Agordejo?
if status . value & jacklib . JackNameNotUnique :
logger . warning ( f " Another JACK client called { METADATA [ ' name ' ] } exist. We will rename ourselve, but this is most likely a real problem. Agordejo is not supposed to be started twice. Please investigate! " )
else :
logger . error ( " JackClient error: " + status . value )
sys . exit ( 0 ) #atexit will trigger
atexit . register ( lambda c = self . _jacklibClient : jacklib . client_close ( c ) )
#Callbacks. They are mirrored by the api Callbacks without the callback_ prefix so a GUI can directly access them.
#However, they are mutable lists. And we define all actual callback-sender here. the api calls them via our Object/Instance
self . callback_setPlaybackSeconds = [ ]
def _setPlaybackSeconds ( self ) :
""" Added to the fast event loop. Therefore called VERY often.
Yes , this is not a super accurate function because while we iterate transport already progresses """
pos = jacklib . jack_position_t ( ) #pos._fields_
state = jacklib . transport_query ( self . _jacklibClient , pointer ( pos ) ) #this actually sets the pos and info. We need this, even if we don't use "state" here.
#if not pos.frame_rate:
# return
currenTimeInSeconds = round ( pos . frame / pos . frame_rate , 8 )
for func in self . callback_setPlaybackSeconds :
func ( currenTimeInSeconds , not state == jacklib . JackTransportStopped ) #current time in seconds and if playback is running
#Public API. Called via api.jackClient.rewind()
def _seek ( self , frame : int ) :
jacklib . transport_locate ( self . _jacklibClient , frame )
def seek ( self , seconds ) :
pos = jacklib . jack_position_t ( )
state = jacklib . transport_query ( self . _jacklibClient , pointer ( pos ) ) #this actually sets the pos and info. We need this, even if we don't use "state" here.
self . _seek ( int ( seconds * pos . frame_rate ) )
def rewind ( self ) :
self . _seek ( 0 )
def playPause ( self ) :
pos = jacklib . jack_position_t ( ) #pos._fields_
state = jacklib . transport_query ( self . _jacklibClient , pointer ( pos ) )
if state == jacklib . JackTransportStopped :
jacklib . transport_start ( self . _jacklibClient )
elif state == jacklib . JackTransportStarting :
pass
else :
jacklib . transport_stop ( self . _jacklibClient )