/* $Id: coresound.c,v 1.9 2006/09/15 15:29:50 toad32767 Exp $ */ /** ** 2005, 2006 by Marco Trillo ** This file is part of UModPlayer, and is released by ** its autors to the Public Domain. ** In case it's not legally possible, its autors grant ** anyone the right to use, redistribute and modify ** this software for any purpose, without any conditions, ** unless such conditions are required by law. ** ** THIS FILE COMES WITHOUT ANY WARRANTY. THE AUTHORS ** SHALL NOT BE LIABLE FOR ANY DAMAGE RESULTING BY THE ** USE OR MISUSE OF THIS SOFTWARE. **/ /* * ===================== * SOUND RELATED STUFF * ===================== */ #include #include #include #include #include #include LOCAL void CoreSound_Monitor(void); LOCAL Bool CoreSound_AudioPause(void); LOCAL uint8_t aubuffer[BUFFER_SIZE]; LOCAL size_t bytes_played = 0; /* * THE CORESOUND ARCHITECTURE * * The CoreSound system is used to play the loaded module (the `song'). * The CoreSound caller must provide it with a special function named `monitor'. * The `monitor' function is used to display information * on screen (or other devices) while the song is being played. * * Different `monitor's are used in different places. In example, * the `notes' system uses a `monitor' which displays the notes on * a row while playing; and the default `monitor' displays the current * position in the song (such as order, row, time, etc.). * * The prototype of a monitor is this: * void Monitor (void); * * The Monitor should consist on a loop which calls regularly the * function `CoreSound_InjectPCM()'. The CoreSound_InjectPCM() function * causes the song position to advance (by calling the appropriate ModPlug * function) and plays some audio samples (defined by the BUFFER_SIZE constant). * * The Monitor should check regularly the `AudioActive' global variable. * This variable will be TRUE when the song is still being played. * When this variable is FALSE, the song has reached its end. * * Before returning, the Monitor should call the `CoreSound_Quit()' function * to close the audio device. The Monitor should do this when the `AudioActive' * variable becomes FALSE. Additionally, the Monitor might provide some other * ways to stop the playing process. * In example, some monitors stop playing when a key is pressed by the user. * * This is an skeleton for a monitor: * * void * MyMonitor () * { * for (;;) { * CoreSound_InjectPCM(); * * if (AudioActive == FALSE) { * CoreSound_Quit(); * return; * } * } * } * * This monitor will simply play the entire song until the end. * * To call CoreSound, use the `CoreSound_Start()' function * passing (a pointer to) your monitor function as a parameter. * In example: * * CoreSound_Start(MyMonitor); * * The `CoreSound_Start()' function will return when the monitor * function returns. * * If you want to use the default monitor which CoreSound provides, * call `CoreSound_StartMonitor()' instead (without parameters). * * This default monitor is used by the `play' and `playlist' commands. */ EXPORT void CoreSound_InitOptions() { ModPlug_Settings MPSettings; MPSettings.mFlags = sets.flags; MPSettings.mChannels = sets.channels; MPSettings.mBits = CoreSound_BitsPerSample; MPSettings.mFrequency = sets.samplerate; MPSettings.mResamplingMode = sets.resampling; MPSettings.mReverbDepth = sets.reverbDepth; MPSettings.mReverbDelay = sets.reverbDelay; MPSettings.mBassAmount = sets.bassAmount; MPSettings.mBassRange = sets.bassRange; MPSettings.mSurroundDepth = sets.surroundDepth; MPSettings.mSurroundDelay = sets.surroundDelay; MPSettings.mLoopCount = 0; ModPlug_SetSettings(&MPSettings); ModPlug_SetMasterVolume(file.mod, sets.vol); } LOCAL Bool CoreSound_InitAudio() { int drv = -1; ao_sample_format fmt; if (AudioDriver) drv = ao_driver_id(AudioDriver); else drv = ao_default_driver_id(); if (drv < 0) return FALSE; fmt.rate = sets.samplerate; fmt.bits = 16; fmt.byte_format = AO_FMT_NATIVE; fmt.channels = sets.channels; AO_Device = ao_open_live(drv, &fmt, 0); if (!AO_Device) { return FALSE; } CoreSound_BitsPerSample = fmt.bits; CoreSound_InitOptions(); AudioActive = TRUE; return TRUE; } EXPORT void CoreSound_InjectPCM() { int rlen; if (!AudioActive) return; rlen = ModPlug_Read(file.mod, aubuffer, BUFFER_SIZE); if (rlen == 0) { CoreSound_CloseAudio(); return; } ao_play(AO_Device, aubuffer, BUFFER_SIZE); bytes_played += BUFFER_SIZE; } EXPORT Bool CoreSound_StartMonitor() { return CoreSound_Start(CoreSound_Monitor); } EXPORT Bool CoreSound_Start(xproc MonitorFunction) { if (file.name == NULL) { return FALSE; } ao_initialize(); if (CoreSound_InitAudio() == FALSE) { error("%s", MESSAGE_AUDIO_ERROR); return FALSE; } bytes_played = 0; MonitorFunction(); return TRUE; } EXPORT int KeyPress() { int keyHit; fd_set fds; struct timeval tv; struct termios term, _term; tcgetattr(0, &_term); memcpy(&term, &_term, sizeof(term)); cfmakeraw(&term); tcsetattr(0, TCSANOW, &term); FD_ZERO(&fds); FD_SET(0, &fds); tv.tv_sec = 0; tv.tv_usec = 0; keyHit = select(1, &fds, NULL, NULL, &tv); tcsetattr(0, TCSANOW, &_term); return keyHit; } LOCAL void CoreSound_Monitor() { unsigned long seconds = 0; unsigned int bytesPerSample; bytesPerSample = CoreSound_BitsPerSample >> 3; /* CoreSound_BitsPerSample / 8 */ puts(MESSAGE_PLAYING); for (;;) { /* * Prepare and inject the PCM samples */ CoreSound_InjectPCM(); /* * Check if we have reached end */ if (AudioActive == FALSE) { CoreSound_Quit(); putchar('\n'); return; } if (KeyPress()) { if (CoreSound_AudioPause() == TRUE) { putchar('\n'); return; /* if key S pressed -> bye */ } notice("%s\n", MESSAGE_PLAYING); CoreSound_InjectPCM(); } /* * Calculate the seconds played */ seconds = ((bytes_played / bytesPerSample) / sets.channels) / (sets.samplerate); notice("{%d:%02d} ord: %d - pat: %d - row: %d - tempo: %d \r", seconds / 60, seconds % 60, ModPlug_GetCurrentOrder(file.mod), ModPlug_GetCurrentPattern(file.mod), ModPlug_GetCurrentRow(file.mod), ModPlug_GetCurrentTempo(file.mod) ); fflush(stdout); } return; } LOCAL Bool CoreSound_AudioPause() { char *K; CoreSound_CloseAudio(); for (;;) { notice("%s", MESSAGE_PAUSED); fflush(stdout); K = ReadString(); if (K == NULL) { CoreSound_Quit(); return TRUE; } if (*K == 'S') { CoreSound_Quit(); free(K); return TRUE; } else if (*K == '\0') { free(K); /* * If CoreSound_InitAudio() fails, return TRUE to quit. */ return ! CoreSound_InitAudio(); } else { free(K); } } return FALSE; } EXPORT void CoreSound_CloseAudio() { ao_close(AO_Device); AudioActive = FALSE; } EXPORT void CoreSound_Quit() { if (AudioActive) CoreSound_CloseAudio(); ao_shutdown(); }