static void CL_PlayVoip(int sender, int samplecnt, const byte* data, int flags) { if (flags & VOIP_DIRECT) { S_RawSamples(sender + 1, samplecnt, clc.speexSampleRate, 2, 1, data, clc.voipGain[sender], -1); } if (flags & VOIP_SPATIAL) { S_RawSamples(sender + MAX_CLIENTS + 1, samplecnt, clc.speexSampleRate, 2, 1, data, 1.0f, sender); } }
static void CL_PlayVoip(int sender, int samplecnt, const byte *data, int flags) { if(flags & VOIP_DIRECT) { S_RawSamples(sender + MAX_STREAMING_SOUNDS, samplecnt, 48000, 2, 1, data, clc.voipGain[sender], -1); } if(flags & VOIP_SPATIAL) { S_RawSamples(sender + MAX_CLIENTS + MAX_STREAMING_SOUNDS, samplecnt, 48000, 2, 1, data, 1.0f, sender); } }
/* * S_HandleRawSamplesCmd */ static unsigned S_HandleRawSamplesCmd( const sndRawSamplesCmd_t *cmd ) { S_RawSamples( cmd->samples, cmd->rate, cmd->width, cmd->channels, cmd->data, cmd->music ); S_Free( ( void * )cmd->data ); return sizeof( *cmd ); }
/* return: audio wants more packets */ static qboolean loadAudio(void) { qboolean anyDataTransferred = qtrue; float **pcm; float *right,*left; int samples, samplesNeeded; int i; short* ptr; ogg_packet op; vorbis_block vb; memset(&op,0,sizeof(op)); memset(&vb,0,sizeof(vb)); vorbis_block_init(&g_ogm.vd,&vb); while(anyDataTransferred && g_ogm.currentTime+MAX_AUDIO_PRELOAD>(int)(g_ogm.vd.granulepos*1000/g_ogm.vi.rate)) { anyDataTransferred =qfalse; if((samples=vorbis_synthesis_pcmout(&g_ogm.vd,&pcm))>0) { // vorbis -> raw ptr = (short*)rawBuffer; samplesNeeded = (SIZEOF_RAWBUFF) / (2*2);// (width*channel) if(samples<samplesNeeded) samplesNeeded = samples; left=pcm[0]; right=(g_ogm.vi.channels>1)?pcm[1]:pcm[0]; for(i=0;i<samplesNeeded;++i) { ptr[0]=(left[i]>=-1.0f && left[i]<=1.0f)?left[i]*32767.f:32767*((left[i]>0.0f)-(left[i]<0.0f)); ptr[1]=(right[i]>=-1.0f && right[i]<=1.0f)?right[i]*32767.f:32767*((right[i]>0.0f)-(right[i]<0.0f)); ptr+=2;//numChans; } if(i>0) { // tell libvorbis how many samples we actually consumed vorbis_synthesis_read(&g_ogm.vd,i); S_RawSamples( 0, i, g_ogm.vi.rate, 2, 2, rawBuffer, 1.0f, -1 ); anyDataTransferred = qtrue; } } if(!anyDataTransferred) { // op -> vorbis if(ogg_stream_packetout(&g_ogm.os_audio,&op)) { if(vorbis_synthesis(&vb,&op)==0) vorbis_synthesis_blockin(&g_ogm.vd,&vb); anyDataTransferred = qtrue; } } } vorbis_block_clear(&vb); if(g_ogm.currentTime+MIN_AUDIO_PRELOAD>(int)(g_ogm.vd.granulepos*1000/g_ogm.vi.rate)) return qtrue; else return qfalse; }
/* ================== SCR_ReadNextFrame ================== */ byte *SCR_ReadNextFrame (void) { int32_t r; int32_t command; byte samples[22050/14*4]; byte compressed[0x20000]; int32_t size; byte *pic; cblock_t in, huf1; int32_t start, end, count; // read the next frame r = FS_FRead (&command, 4, 1, cl.cinematic_file); if (r == 0) // we'll give it one more chance r = FS_FRead (&command, 4, 1, cl.cinematic_file); if (r != 4) return NULL; command = LittleLong(command); if (command == 2) return NULL; // last frame marker if (command == 1) { // read palette FS_Read (cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file); cl.cinematicpalette_active = 0; // dubious.... exposes an edge case } // decompress the next frame FS_Read (&size, 4, cl.cinematic_file); size = LittleLong(size); if (size > sizeof(compressed) || size < 1) Com_Error (ERR_DROP, "Bad compressed frame size"); FS_Read (compressed, size, cl.cinematic_file); // read sound start = cl.cinematicframe*cin.s_rate/14; end = (cl.cinematicframe+1)*cin.s_rate/14; count = end - start; FS_Read (samples, count*cin.s_width*cin.s_channels, cl.cinematic_file); S_RawSamples (count, cin.s_rate, cin.s_width, cin.s_channels, samples, Cvar_VariableValue("s_volume")); in.data = compressed; in.count = size; huf1 = Huff1Decompress (in); pic = huf1.data; cl.cinematicframe++; return pic; }
/* ================== CIN_DecodeSoundMono ================== */ static void CIN_DecodeSoundMono (cinematic_t *cin, const byte *data){ int prev; int i; if (cin->flags & CIN_SILENT) return; prev = cin->chunk.flags; for (i = 0; i < cin->chunk.size; i++){ prev = (short)(prev + cin_sqrTable[data[i]]); cin_soundSamples[i] = (short)prev; } // Submit the sound samples S_RawSamples(cin_soundSamples, cin->chunk.size, 22050, false, 1.0f); }
/* * Play a portion of the currently opened file. */ int OGG_Read ( void ) { int res; /* Number of bytes read. */ /* Read and resample. */ res = ov_read( &ovFile, ovBuf, sizeof ( ovBuf ), ogg_bigendian, OGG_SAMPLEWIDTH, 1, &ovSection ); S_RawSamples( res / (OGG_SAMPLEWIDTH * ogg_info->channels), ogg_info->rate, OGG_SAMPLEWIDTH, ogg_info->channels, (byte *) ovBuf, ogg_volume->value ); /* Check for end of file. */ if ( res == 0 ) { OGG_Stop(); OGG_Sequence(); } return ( res ); }
/* ============ S_StreamBackgroundTrack ============ */ void S_StreamBackgroundTrack (void) { int samples, maxSamples; int read, maxRead, total, dummy; float scale; byte data[MAX_RAW_SAMPLES*4]; if (!s_bgTrack.file || !s_musicvolume->value) return; if (!s_streamingChannel) return; if (s_rawend < paintedtime) s_rawend = paintedtime; scale = (float)s_bgTrack.rate / dma.speed; maxSamples = sizeof(data) / s_bgTrack.channels / s_bgTrack.width; while (1) { samples = (paintedtime + MAX_RAW_SAMPLES - s_rawend) * scale; if (samples <= 0) return; if (samples > maxSamples) samples = maxSamples; maxRead = samples * s_bgTrack.channels * s_bgTrack.width; total = 0; while (total < maxRead) { read = ov_read(s_bgTrack.vorbisFile, data + total, maxRead - total, 0, 2, 1, &dummy); if (!read) { // End of file if (!s_bgTrack.looping) { // Close the intro track S_CloseBackgroundTrack(&s_bgTrack); // Open the loop track if (!S_OpenBackgroundTrack(s_bgTrack.loopName, &s_bgTrack)) { S_StopBackgroundTrack(); return; } s_bgTrack.looping = true; } else { // check if it's time to switch to the ambient track if ( ++ogg_loopcounter >= (int)ogg_loopcount->value && (!cl.configstrings[CS_MAXCLIENTS][0] || !strcmp(cl.configstrings[CS_MAXCLIENTS], "1")) ) { // Close the loop track S_CloseBackgroundTrack(&s_bgTrack); if (!S_OpenBackgroundTrack(s_bgTrack.ambientName, &s_bgTrack)) { if (!S_OpenBackgroundTrack(s_bgTrack.loopName, &s_bgTrack)) { S_StopBackgroundTrack(); return; } } else s_bgTrack.ambient_looping = true; } } // Restart the track, skipping over the header ov_raw_seek(s_bgTrack.vorbisFile, (ogg_int64_t)s_bgTrack.start); } /*if (s_bgTrack.read) read = s_bgTrack->read( s_bgTrack, data + total, maxRead - total ); else read = FS_Read( data + total, maxRead - total, s_bgTrack->file ); if (!read) { if (s_bgTrackIntro.file != s_bgTrackLoop.file) { if (s_bgTrackIntro.close) s_bgTrackIntro.close(&s_bgTrackIntro); else FS_FCloseFile(s_bgTrackIntro.file); s_bgTrackIntro = s_bgTrackLoop; } s_bgTrack = &s_bgTrackLoop; if (s_bgTrack->seek) s_bgTrack->seek( s_bgTrack, s_bgTrack->info.dataofs ); else FS_Seek(s_bgTrack->file, s_bgTrack->info.dataofs, FS_SEEK_SET); }*/ total += read; } S_RawSamples (samples, s_bgTrack.rate, s_bgTrack.width, s_bgTrack.channels, data, true ); } }
/* ================= S_StreamBackgroundTrack ================= */ void S_StreamBackgroundTrack (void) { byte data[BUFFER_SIZE]; int queued = 0; //, processed, state; int size, read, dummy; //unsigned buffer; int samples; // Knightmare added if (!s_bgTrack.file || !s_musicvolume->value) return; if (!s_streamingChannel) return; // Unqueue and delete any processed buffers /*qalGetSourcei(s_streamingChannel->sourceNum, AL_BUFFERS_PROCESSED, &processed); if (processed > 0){ while (processed--){ qalSourceUnqueueBuffers(s_streamingChannel->sourceNum, 1, &buffer); qalDeleteBuffers(1, &buffer); } }*/ //Com_Printf("Streaming background track\n"); // Make sure we always have at least 4 buffers in the queue //qalGetSourcei(s_streamingChannel->sourceNum, AL_BUFFERS_QUEUED, &queued); while (queued < 4) { size = 0; // Stream from disk while (size < BUFFER_SIZE) { read = ov_read(s_bgTrack.vorbisFile, data + size, BUFFER_SIZE - size, 0, 2, 1, &dummy); if (read == 0) { // End of file if (!s_bgTrack.looping) { // Close the intro track S_CloseBackgroundTrack(&s_bgTrack); // Open the loop track if (!S_OpenBackgroundTrack(s_bgTrack.loopName, &s_bgTrack)) { S_StopBackgroundTrack(); return; } s_bgTrack.looping = true; } // Restart the track, skipping over the header ov_raw_seek(s_bgTrack.vorbisFile, (ogg_int64_t)s_bgTrack.start); // Try streaming again read = ov_read(s_bgTrack.vorbisFile, data + size, BUFFER_SIZE - size, 0, 2, 1, &dummy); } if (read <= 0) { // An error occurred S_StopBackgroundTrack(); return; } size += read; } // Knightmare added samples = size / (s_bgTrack.width * s_bgTrack.channels); S_RawSamples(samples, s_bgTrack.rate,s_bgTrack. width, s_bgTrack.channels, data, true); // Upload and queue the new buffer /*qalGenBuffers(1, &buffer); qalBufferData(buffer, s_bgTrack.format, data, size, s_bgTrack.rate); qalSourceQueueBuffers(s_streamingChannel->sourceNum, 1, &buffer);*/ queued++; } // Update volume //qalSourcef(s_streamingChannel->sourceNum, AL_GAIN, s_musicVolume->value); // If not playing, then do so /*qalGetSourcei(s_streamingChannel->sourceNum, AL_SOURCE_STATE, &state); if (state != AL_PLAYING) qalSourcePlay(s_streamingChannel->sourceNum);*/ }
/* return: audio wants more packets */ static qboolean OGV_LoadAudio(cinematic_t *cin) { qboolean anyDataTransferred = qtrue; float **pcm; int frames, frameNeeded; int i, j; short *ptr; ogg_packet op; vorbis_block vb; memset(&op, 0, sizeof(op)); memset(&vb, 0, sizeof(vb)); vorbis_block_init(&g_ogm->vd, &vb); while (anyDataTransferred && g_ogm->currentTime + MAX_AUDIO_PRELOAD > (int)(g_ogm->vd.granulepos * 1000 / g_ogm->vi.rate)) { anyDataTransferred = qfalse; if ((frames = vorbis_synthesis_pcmout(&g_ogm->vd, &pcm)) > 0) { // vorbis -> raw ptr = (short *)g_ogm->audioBuffer; frameNeeded = (SIZEOF_RAWBUFF) / (OGG_SAMPLEWIDTH * g_ogm->vi.channels); if (frames < frameNeeded) { frameNeeded = frames; } for (i = 0; i < frameNeeded; i++) { for (j = 0; j < g_ogm->vi.channels; j++) { *(ptr++) = (short)((pcm[j][i] >= -1.0f && pcm[j][i] <= 1.0f) ? pcm[j][i] * 32767.f : 32767 * ((pcm[j][i] > 0.0f) - (pcm[j][i] < 0.0f))); } } // tell libvorbis how many samples we actually consumed (we ate them all!) vorbis_synthesis_read(&g_ogm->vd, frameNeeded); if (!(cin->flags & CIN_silent)) { S_RawSamples(0, frameNeeded, g_ogm->vi.rate, OGG_SAMPLEWIDTH, g_ogm->vi.channels, g_ogm->audioBuffer, 1.0f, 1.0f); } anyDataTransferred = qtrue; } if (!anyDataTransferred) { // op -> vorbis if (ogg_stream_packetout(&g_ogm->os_audio, &op)) { if (vorbis_synthesis(&vb, &op) == 0) { vorbis_synthesis_blockin(&g_ogm->vd, &vb); } anyDataTransferred = qtrue; } } } vorbis_block_clear(&vb); return (qboolean)(g_ogm->currentTime + MIN_AUDIO_PRELOAD > (int)(g_ogm->vd.granulepos * 1000 / g_ogm->vi.rate)); }
/* ============ S_StreamBackgroundTrack ============ */ void S_StreamBackgroundTrack (void) { int samples, maxSamples; int read, maxRead, total, dummy; float scale; char data[MAX_RAW_SAMPLES*4]; if (!ogg_started) return; if (!s_bgTrack.buffer || !s_musicvolume->value) return; if (!s_streamingChannel) return; if (s_rawend < paintedtime) s_rawend = paintedtime; scale = (float)s_bgTrack.rate / dma.speed; maxSamples = sizeof(data) / s_bgTrack.channels / s_bgTrack.width; while (1) { samples = (paintedtime + MAX_RAW_SAMPLES - s_rawend) * scale; if (samples <= 0) return; if (samples > maxSamples) samples = maxSamples; maxRead = samples * s_bgTrack.channels * s_bgTrack.width; total = 0; while (total < maxRead) { read = ov_read((OggVorbis_File*)s_bgTrack.vorbisFile, data + total, maxRead - total, 0, 2, 1, &dummy); if (!read) { // end of file if (!s_bgTrack.looping) { // close the intro track S_CloseBackgroundTrack(&s_bgTrack); // open the loop track if (!S_OpenBackgroundTrack(s_bgTrack.loopName, &s_bgTrack)) { S_StopBackgroundTrack(); return; } s_bgTrack.looping = true; } else { // check if its time to switch to the ambient track if ( ++ogg_loopcounter >= (int)ogg_loopcount->value && (!cl.configstrings[CS_MAXCLIENTS][0] || !strcmp(cl.configstrings[CS_MAXCLIENTS], "1")) ) { // close the loop track S_CloseBackgroundTrack(&s_bgTrack); if (!S_OpenBackgroundTrack(s_bgTrack.ambientName, &s_bgTrack)) { if (!S_OpenBackgroundTrack(s_bgTrack.loopName, &s_bgTrack)) { S_StopBackgroundTrack(); return; } } else s_bgTrack.ambient_looping = true; } } // restart the track, skipping over the header ov_raw_seek((OggVorbis_File*)s_bgTrack.vorbisFile, (ogg_int64_t)s_bgTrack.start); } total += read; } S_RawSamples (samples, s_bgTrack.rate, s_bgTrack.width, s_bgTrack.channels, data, true); } }
/* ====================== S_UpdateBackgroundTrack ====================== */ void S_UpdateBackgroundTrack( void ) { int bufferSamples; int fileSamples; byte raw[30000]; // just enough to fit in a mac stack frame int fileBytes; int r; static float musicVolume = 0.5f; if ( !s_backgroundFile ) { return; } // graeme see if this is OK musicVolume = (musicVolume + (s_musicVolume->value * 2))/4.0f; // don't bother playing anything if musicvolume is 0 if ( musicVolume <= 0 ) { return; } // see how many samples should be copied into the raw buffer if ( s_rawend < s_soundtime ) { s_rawend = s_soundtime; } while ( s_rawend < s_soundtime + MAX_RAW_SAMPLES ) { bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime); // decide how much data needs to be read from the file fileSamples = bufferSamples * s_backgroundInfo.rate / dma.speed; // don't try and read past the end of the file if ( fileSamples > s_backgroundSamples ) { fileSamples = s_backgroundSamples; } // our max buffer size fileBytes = fileSamples * (s_backgroundInfo.width * s_backgroundInfo.channels); if ( fileBytes > sizeof(raw) ) { fileBytes = sizeof(raw); fileSamples = fileBytes / (s_backgroundInfo.width * s_backgroundInfo.channels); } r = Sys_StreamedRead( raw, 1, fileBytes, s_backgroundFile ); if ( r != fileBytes ) { Com_Printf("StreamedRead failure on music track\n"); S_StopBackgroundTrack(); return; } // byte swap if needed S_ByteSwapRawSamples( fileSamples, s_backgroundInfo.width, s_backgroundInfo.channels, raw ); // add to raw buffer S_RawSamples( fileSamples, s_backgroundInfo.rate, s_backgroundInfo.width, s_backgroundInfo.channels, raw, musicVolume ); s_backgroundSamples -= fileSamples; if ( !s_backgroundSamples ) { // loop if (s_backgroundLoop[0]) { Sys_EndStreamedFile( s_backgroundFile ); FS_FCloseFile( s_backgroundFile ); s_backgroundFile = 0; S_StartBackgroundTrack( s_backgroundLoop, s_backgroundLoop ); if ( !s_backgroundFile ) { return; // loop failed to restart } } else { s_backgroundFile = 0; return; } } } }
/* ===================== CL_ParseVoip A VoIP message has been received from the server ===================== */ static void CL_ParseVoip ( msg_t *msg ) { static short decoded[4096]; // !!! FIXME: don't hardcode. const int sender = MSG_ReadShort(msg); const int generation = MSG_ReadByte(msg); const int sequence = MSG_ReadLong(msg); const int frames = MSG_ReadByte(msg); const int packetsize = MSG_ReadShort(msg); char encoded[1024]; int seqdiff = sequence - clc.voipIncomingSequence[sender]; int written = 0; int i; Com_DPrintf("VoIP: %d-byte packet from client %d\n", packetsize, sender); if (sender < 0) return; // short/invalid packet, bail. else if (generation < 0) return; // short/invalid packet, bail. else if (sequence < 0) return; // short/invalid packet, bail. else if (frames < 0) return; // short/invalid packet, bail. else if (packetsize < 0) return; // short/invalid packet, bail. if (packetsize > sizeof (encoded)) { // overlarge packet? int bytesleft = packetsize; while (bytesleft) { int br = bytesleft; if (br > sizeof (encoded)) br = sizeof (encoded); MSG_ReadData(msg, encoded, br); bytesleft -= br; } return; // overlarge packet, bail. } if (!clc.speexInitialized) { MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // can't handle VoIP without libspeex! } else if (sender >= MAX_CLIENTS) { MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // bogus sender. } else if (CL_ShouldIgnoreVoipSender(sender)) { MSG_ReadData(msg, encoded, packetsize); // skip payload. return; // Channel is muted, bail. } // !!! FIXME: make sure data is narrowband? Does decoder handle this? Com_DPrintf("VoIP: packet accepted!\n"); // This is a new "generation" ... a new recording started, reset the bits. if (generation != clc.voipIncomingGeneration[sender]) { Com_DPrintf("VoIP: new generation %d!\n", generation); speex_bits_reset(&clc.speexDecoderBits[sender]); clc.voipIncomingGeneration[sender] = generation; seqdiff = 0; } else if (seqdiff < 0) { // we're ahead of the sequence?! // This shouldn't happen unless the packet is corrupted or something. Com_DPrintf("VoIP: misordered sequence! %d < %d!\n", sequence, clc.voipIncomingSequence[sender]); // reset the bits just in case. speex_bits_reset(&clc.speexDecoderBits[sender]); seqdiff = 0; } else if (seqdiff > 100) { // more than 2 seconds of audio dropped? // just start over. Com_DPrintf("VoIP: Dropped way too many (%d) frames from client #%d\n", seqdiff, sender); speex_bits_reset(&clc.speexDecoderBits[sender]); seqdiff = 0; } if (seqdiff != 0) { Com_DPrintf("VoIP: Dropped %d frames from client #%d\n", seqdiff, sender); // tell speex that we're missing frames... for (i = 0; i < seqdiff; i++) { assert((written + clc.speexFrameSize) * 2 < sizeof (decoded)); speex_decode_int(clc.speexDecoder[sender], NULL, decoded + written); written += clc.speexFrameSize; } } for (i = 0; i < frames; i++) { char encoded[256]; const int len = MSG_ReadByte(msg); if (len < 0) { Com_DPrintf("VoIP: Short packet!\n"); break; } MSG_ReadData(msg, encoded, len); // shouldn't happen, but just in case... if ((written + clc.speexFrameSize) * 2 > sizeof (decoded)) { Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i); S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1, (const byte *) decoded, clc.voipGain[sender]); written = 0; } speex_bits_read_from(&clc.speexDecoderBits[sender], encoded, len); speex_decode_int(clc.speexDecoder[sender], &clc.speexDecoderBits[sender], decoded + written); #if 0 static FILE *encio = NULL; if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } static FILE *decio = NULL; if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } #endif written += clc.speexFrameSize; } Com_DPrintf("VoIP: playback %d bytes, %d samples, %d frames\n", written * 2, written, i); if (written > 0) { S_RawSamples(sender + 1, written, clc.speexSampleRate, 2, 1, (const byte *) decoded, clc.voipGain[sender]); } clc.voipIncomingSequence[sender] = sequence + frames; }
static void BGM_UpdateStream (void) { int res; /* Number of bytes read. */ int bufferSamples; int fileSamples; int fileBytes; byte raw[16384]; if (bgmstream->status != STREAM_PLAY) return; /* don't bother playing anything if musicvolume is 0 */ if (bgmvolume.value <= 0) return; /* see how many samples should be copied into the raw buffer */ if (s_rawend < paintedtime) s_rawend = paintedtime; while (s_rawend < paintedtime + MAX_RAW_SAMPLES) { bufferSamples = MAX_RAW_SAMPLES - (s_rawend - paintedtime); /* decide how much data needs to be read from the file */ fileSamples = bufferSamples * bgmstream->info.rate / shm->speed; if (!fileSamples) return; /* our max buffer size */ fileBytes = fileSamples * (bgmstream->info.width * bgmstream->info.channels); if (fileBytes > (int) sizeof(raw)) { fileBytes = (int) sizeof(raw); fileSamples = fileBytes / (bgmstream->info.width * bgmstream->info.channels); } /* Read */ res = S_CodecReadStream(bgmstream, fileBytes, raw); if (res < fileBytes) { fileBytes = res; fileSamples = res / (bgmstream->info.width * bgmstream->info.channels); } if (res > 0) /* data: add to raw buffer */ { S_RawSamples(fileSamples, bgmstream->info.rate, bgmstream->info.width, bgmstream->info.channels, raw, bgmvolume.value); } else if (res == 0) /* EOF */ { if (bgmloop) { if (S_CodecRewindStream(bgmstream) < 0) { BGM_Stop(); return; } } else { BGM_Stop(); return; } } else /* res < 0: some read error */ { Con_Printf("Stream read error (%i), stopping.\n", res); BGM_Stop(); return; } } }
byte * SCR_ReadNextFrame(void) { int r; int command; byte samples[22050 / 14 * 4]; byte compressed[0x20000]; int size; byte *pic; cblock_t in, huf1; int start, end, count; /* read the next frame */ r = FS_FRead(&command, 4, 1, cl.cinematic_file); if (r == 0) { /* we'll give it one more chance */ r = FS_FRead(&command, 4, 1, cl.cinematic_file); } if (r != 4) { return NULL; } command = LittleLong(command); if (command == 2) { return NULL; /* last frame marker */ } if (command == 1) { /* read palette */ FS_Read(cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file); cl.cinematicpalette_active = 0; } /* decompress the next frame */ FS_Read(&size, 4, cl.cinematic_file); size = LittleLong(size); if (((unsigned long)size > sizeof(compressed)) || (size < 1)) { Com_Error(ERR_DROP, "Bad compressed frame size"); } FS_Read(compressed, size, cl.cinematic_file); /* read sound */ start = cl.cinematicframe * cin.s_rate / 14; end = (cl.cinematicframe + 1) * cin.s_rate / 14; count = end - start; FS_Read(samples, count * cin.s_width * cin.s_channels, cl.cinematic_file); if (cin.s_width == 2) { for (r = 0; r < count * cin.s_channels; r++) { ((short *)samples)[r] = LittleShort(((short *)samples)[r]); } } S_RawSamples(count, cin.s_rate, cin.s_width, cin.s_channels, samples, Cvar_VariableValue("s_volume")); in.data = compressed; in.count = size; huf1 = Huff1Decompress(in); pic = huf1.data; cl.cinematicframe++; return pic; }