int PlayerObject::setPlaying(bool playing) {
    PlayerObject* player = this;
    bool oldplayingstate = false;
    PyScopedGIL gil;
    {
        PyScopedGIUnlock gunlock;

        player->workerThread.start(); // if not running yet, start
        if(!player->outStream.get())
            player->outStream.reset(new OutStream(this));
        assert(player->outStream.get() != NULL);

        if(soundcardOutputEnabled) {
            if(playing && !player->outStream->stream) {
                if(!player->outStream->open())
                    playing = false;
            }
        }

        oldplayingstate = player->playing;
        if(soundcardOutputEnabled && player->outStream.get() && player->outStream->isOpen() && oldplayingstate != playing)
            fader.init(playing ? 1 : -1, outSamplerate);
        player->playing = playing;
    }

    if(!PyErr_Occurred() && player->dict) {
        Py_INCREF(player->dict);
        PyObject* onPlayingStateChange = PyDict_GetItemString(player->dict, "onPlayingStateChange");
        if(onPlayingStateChange && onPlayingStateChange != Py_None) {
            Py_INCREF(onPlayingStateChange);

            PyObject* kwargs = PyDict_New();
            assert(kwargs);
            PyDict_SetItemString_retain(kwargs, "oldState", PyBool_FromLong(oldplayingstate));
            PyDict_SetItemString_retain(kwargs, "newState", PyBool_FromLong(playing));

            PyObject* retObj = PyEval_CallObjectWithKeywords(onPlayingStateChange, NULL, kwargs);
            Py_XDECREF(retObj);

            // errors are not fatal from the callback, so handle it now and go on
            if(PyErr_Occurred())
                PyErr_Print();

            Py_DECREF(kwargs);
            Py_DECREF(onPlayingStateChange);
        }
        Py_DECREF(player->dict);
    }

    return PyErr_Occurred() ? -1 : 0;
}
int PlayerObject::setPlaying(bool playing) {
	PlayerObject* player = this;
	bool oldplayingstate = false;
	PyScopedGIL gil;
	{
		PyScopedGIUnlock gunlock;
		
		player->workerThread.start(); // if not running yet, start
		if(!player->outStream.get())
			player->outStream.reset(new OutStream(this));
		assert(player->outStream.get() != NULL);
		if(playing && !player->outStream->stream) {
			PaError ret;
			ret = Pa_OpenDefaultStream(
									   &player->outStream->stream,
									   0,
									   player->outNumChannels, // numOutputChannels
									   paInt16, // sampleFormat
									   player->outSamplerate, // sampleRate
									   AUDIO_BUFFER_SIZE / 2, // framesPerBuffer,
									   &paStreamCallback,
									   player //void *userData
									   );
			if(ret != paNoError) {
				PyErr_SetString(PyExc_RuntimeError, "Pa_OpenDefaultStream failed");
				if(player->outStream->stream)
					player->outStream->close();
				playing = 0;
			}
		}
		if(playing) {
			player->needRealtimeReset = 1;
			Pa_StartStream(player->outStream->stream);
		} else
			player->outStream->stop();
		oldplayingstate = player->playing;
		player->playing = playing;
	}
	
	if(!PyErr_Occurred() && player->dict) {
		Py_INCREF(player->dict);
		PyObject* onPlayingStateChange = PyDict_GetItemString(player->dict, "onPlayingStateChange");
		if(onPlayingStateChange && onPlayingStateChange != Py_None) {
			Py_INCREF(onPlayingStateChange);
			
			PyObject* kwargs = PyDict_New();
			assert(kwargs);
			PyDict_SetItemString_retain(kwargs, "oldState", PyBool_FromLong(oldplayingstate));
			PyDict_SetItemString_retain(kwargs, "newState", PyBool_FromLong(playing));
			
			PyObject* retObj = PyEval_CallObjectWithKeywords(onPlayingStateChange, NULL, kwargs);
			Py_XDECREF(retObj);
			
			// errors are not fatal from the callback, so handle it now and go on
			if(PyErr_Occurred())
				PyErr_Print();
			
			Py_DECREF(kwargs);
			Py_DECREF(onPlayingStateChange);
		}
		Py_DECREF(player->dict);
	}
	
	return PyErr_Occurred() ? -1 : 0;
}