#define PY_SSIZE_T_CLEAN #include "Python.h" #include TRACK_HEADER #include #include #include #include SUsample sound_buffer[SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT]; #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #define WIN32_EXTRA_LEAN #include #include "mmsystem.h" #include "mmreg.h" #define CINTERFACE #include static WAVEFORMATEX wave_format = { #ifdef SU_SAMPLE_FLOAT WAVE_FORMAT_IEEE_FLOAT, #else WAVE_FORMAT_PCM, #endif SU_CHANNEL_COUNT, SU_SAMPLE_RATE, SU_SAMPLE_RATE * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT, SU_SAMPLE_SIZE * SU_CHANNEL_COUNT, SU_SAMPLE_SIZE*8, 0 }; DSBUFFERDESC buffer_description = { sizeof(DSBUFFERDESC), DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_TRUEPLAYPOSITION, SU_LENGTH_IN_SAMPLES * SU_SAMPLE_SIZE * SU_CHANNEL_COUNT, 0, &wave_format, 0 }; static HWND hWnd; static LPDIRECTSOUND direct_sound; static LPDIRECTSOUNDBUFFER direct_sound_buffer; static LPVOID p1; static DWORD l1; static DWORD last_play_cursor = 0; #endif /* WIN32 */ #ifdef UNIX #include #include #include static snd_pcm_t *pcm_handle; static pthread_t render_thread; static uint32_t render_thread_handle; static pthread_t playback_thread; static uint32_t playback_thread_handle; snd_htimestamp_t start_ts; static int _snd_pcm_writei(void *params) { (void) params; snd_pcm_writei(pcm_handle, sound_buffer, SU_LENGTH_IN_SAMPLES); return 0; } #endif /* UNIX */ static PyObject *sointuError; static PyObject *sointu_play_song(PyObject *self, PyObject *args) { #ifdef WIN32 #ifdef SU_LOAD_GMDLS su_load_gmdls(); #endif // SU_LOAD_GMDLS hWnd = GetForegroundWindow(); if(hWnd == NULL) { hWnd = GetDesktopWindow(); } DirectSoundCreate(0, &direct_sound, 0); IDirectSound_SetCooperativeLevel(direct_sound, hWnd, DSSCL_PRIORITY); IDirectSound_CreateSoundBuffer(direct_sound, &buffer_description, &direct_sound_buffer, NULL); IDirectSoundBuffer_Lock(direct_sound_buffer, 0, SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE, &p1, &l1, NULL, NULL, 0); CreateThread(0, 0, (LPTHREAD_START_ROUTINE)su_render_song, p1, 0, 0); IDirectSoundBuffer_Play(direct_sound_buffer, 0, 0, 0); #endif /* WIN32 */ #ifdef UNIX render_thread_handle = pthread_create(&render_thread, 0, (void * (*)(void *))su_render_song, sound_buffer); // We can't start playing too early or the missing samples will be audible. sleep(2.); // Play the track. snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0); snd_pcm_set_params( pcm_handle, #ifdef SU_SAMPLE_FLOAT SND_PCM_FORMAT_FLOAT, #else // SU_SAMPLE_FLOAT SND_PCM_FORMAT_S16_LE, #endif // SU_SAMPLE_FLOAT SND_PCM_ACCESS_RW_INTERLEAVED, SU_CHANNEL_COUNT, SU_SAMPLE_RATE, 0, SU_LENGTH_IN_SAMPLES ); // Enable playback time querying. snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(pcm_handle, swparams); snd_pcm_sw_params_set_tstamp_mode(pcm_handle, swparams, SND_PCM_TSTAMP_ENABLE); snd_pcm_sw_params_set_tstamp_type(pcm_handle, swparams, SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY); snd_pcm_sw_params(pcm_handle, swparams); playback_thread_handle = pthread_create(&playback_thread, 0, (void *(*)(void *))_snd_pcm_writei, 0); // Get the start time stamp. snd_pcm_uframes_t avail; snd_pcm_htimestamp(pcm_handle, &avail, &start_ts); #endif /* UNIX */ return PyLong_FromLong(0); } static PyObject *sointu_playback_position(PyObject *self, PyObject *args) { #ifdef WIN32 DWORD play_cursor = 0; IDirectSoundBuffer_GetCurrentPosition(direct_sound_buffer, (DWORD*)&play_cursor, NULL); return Py_BuildValue("i", play_cursor / SU_CHANNEL_COUNT / sizeof(SUsample)); #endif /* WIN32 */ #ifdef UNIX snd_htimestamp_t ts; snd_pcm_uframes_t avail; snd_pcm_htimestamp(pcm_handle, &avail, &ts); return Py_BuildValue("i", (int)((ts.tv_sec - start_ts.tv_sec + 1.e-9 * (ts.tv_nsec - start_ts.tv_nsec)) * SU_SAMPLE_RATE)); #endif /* UNIX */ } static PyObject *sointu_playback_finished(PyObject *self, PyObject *args) { bool result = false; #ifdef WIN32 DWORD play_cursor = 0; IDirectSoundBuffer_GetCurrentPosition(direct_sound_buffer, (DWORD*)&play_cursor, NULL); result = play_cursor < last_play_cursor; last_play_cursor = play_cursor; #endif /* WIN32 */ #ifdef UNIX snd_htimestamp_t ts; snd_pcm_uframes_t avail; snd_pcm_htimestamp(pcm_handle, &avail, &ts); result = ts.tv_sec - start_ts.tv_sec < 0; #endif /* UNIX */ return PyBool_FromLong(result); } static PyObject *sointu_sample_rate(PyObject *self, PyObject *args) { return Py_BuildValue("i", SU_SAMPLE_RATE); } static PyObject *sointu_track_length(PyObject *self, PyObject *args) { return Py_BuildValue("i", SU_LENGTH_IN_SAMPLES); } static PyMethodDef sointuMethods[] = { {"play_song", sointu_play_song, METH_VARARGS, "Play sointu track."}, {"playback_position", sointu_playback_position, METH_VARARGS, "Get playback position of sointu track currently playing."}, {"playback_finished", sointu_playback_finished, METH_VARARGS, "Check if currently playing sointu track has finished playing."}, {"sample_rate", sointu_sample_rate, METH_VARARGS, "Return the sample rate of the track compiled into this module."}, {"track_length", sointu_track_length, METH_VARARGS, "Return the track length in samples."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; static struct PyModuleDef sointumodule = { PyModuleDef_HEAD_INIT, "sointu", NULL, -1, sointuMethods }; PyMODINIT_FUNC PyInit_sointu(void) { PyObject *module = PyModule_Create(&sointumodule); if(module == NULL) { return NULL; } sointuError = PyErr_NewException("sointu.sointuError", NULL, NULL); Py_XINCREF(sointuError); if(PyModule_AddObject(module, "error", sointuError) < 0) { Py_XDECREF(sointuError); Py_CLEAR(sointuError); Py_DECREF(module); return NULL; } return module; }