Example #1
0
    bool Encode(float *input, UINT numInputFrames, DataPacket &packet, QWORD &timestamp)
    {
        if(bFirstFrame)
        {
            curEncodeTimestamp = timestamp;
            bFirstFrame = false;
        }

        //------------------------------------------------

        UINT lastSampleSize = frameCounter;

        frameCounter += numInputFrames;
        if(frameCounter > outputFrameSize)
        {
            frameCounter -= outputFrameSize;

            bufferedTimestamps << curEncodeTimestamp;
            curEncodeTimestamp = timestamp + ((outputFrameSize-lastSampleSize)*1000/App->GetSampleRateHz());
        }

        int ret = lame_encode_buffer_interleaved_ieee_float(lgf, (float*)input, numInputFrames, MP3OutputBuffer.Array()+1, dwMP3MaxSize);

        if(ret < 0)
        {
            AppWarning(TEXT("MP3 encode failed"));
            return false;
        }

        if(ret > 0)
        {
            if(bFirstPacket)
            {
                header.CopyArray(MP3OutputBuffer.Array(), ret);
                bFirstPacket = false;
                ret = 0;
            }
            else
            {
                packet.lpPacket = MP3OutputBuffer.Array();
                packet.size     = ret+1;

                timestamp = bufferedTimestamps[0];
                bufferedTimestamps.Remove(0);
            }
        }

        return ret > 0;
    }
Example #2
0
    ~MP4FileStream()
    {
        if(!bStreamOpened)
            return;

        App->EnableSceneSwitching(false);

        //---------------------------------------------------

        //HWND hwndProgressDialog = CreateDialog(hinstMain, MAKEINTRESOURCE(IDD_BUILDINGMP4), hwndMain, (DLGPROC)MP4ProgressDialogProc);
        //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETRANGE32, 0, 100);

        mdatStop = fileOut.GetPos();

        BufferOutputSerializer output(endBuffer);

        //set a reasonable initial buffer size
        endBuffer.SetSize((videoFrames.Num() + audioFrames.Num()) * 20 + 131072);

        UINT64 audioFrameSize = App->GetAudioEncoder()->GetFrameSize();

        DWORD macTime = fastHtonl(DWORD(GetMacTime()));
        UINT videoDuration = fastHtonl(lastVideoTimestamp + App->GetFrameTime());
        UINT audioDuration = fastHtonl(lastVideoTimestamp + DWORD(double(audioFrameSize)*1000.0/double(App->GetSampleRateHz())));
        UINT width, height;
        App->GetOutputSize(width, height);

        LPCSTR lpVideoTrack = "Video Media Handler";
        LPCSTR lpAudioTrack = "Sound Media Handler";

        const char videoCompressionName[31] = "AVC Coding";

        //-------------------------------------------
        // get video headers
        DataPacket videoHeaders;
        App->GetVideoHeaders(videoHeaders);
        List<BYTE> SPS, PPS;

        LPBYTE lpHeaderData = videoHeaders.lpPacket+11;
        SPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData));

        lpHeaderData += SPS.Num()+3;
        PPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData));

        //-------------------------------------------
        // get AAC headers if using AAC
        List<BYTE> AACHeader;
        if(!bMP3)
        {
            DataPacket data;
            App->GetAudioHeaders(data);
            AACHeader.CopyArray(data.lpPacket+2, data.size-2);
        }

        //-------------------------------------------

        EndChunkInfo(videoChunks, videoSampleToChunk, curVideoChunkOffset, numVideoSamples);
        EndChunkInfo(audioChunks, audioSampleToChunk, curAudioChunkOffset, numAudioSamples);

        if (numVideoSamples > 1)
            GetVideoDecodeTime(videoFrames.Last(), true);

        if (numAudioSamples > 1)
            GetAudioDecodeTime(audioFrames.Last(), true);

        UINT audioUnitDuration = fastHtonl(UINT(lastAudioTimeVal));

        //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 25, 0);

        //-------------------------------------------
        // sound descriptor thingy.  this part made me die a little inside admittedly.
        UINT maxBitRate = fastHtonl(App->GetAudioEncoder()->GetBitRate()*1000);

        List<BYTE> esDecoderDescriptor;
        BufferOutputSerializer esDecoderOut(esDecoderDescriptor);
        esDecoderOut.OutputByte(bMP3 ? 107 : 64);
        esDecoderOut.OutputByte(0x15); //stream/type flags.  always 0x15 for my purposes.
        esDecoderOut.OutputByte(0); //buffer size, just set it to 1536 for both mp3 and aac
        esDecoderOut.OutputWord(WORD_BE(0x600)); 
        esDecoderOut.OutputDword(maxBitRate); //max bit rate (cue bill 'o reily meme for these two)
        esDecoderOut.OutputDword(maxBitRate); //avg bit rate

        if(!bMP3) //if AAC, put in headers
        {
            esDecoderOut.OutputByte(0x5);  //decoder specific descriptor type
            /*esDecoderOut.OutputByte(0x80); //some stuff that no one should probably care about
            esDecoderOut.OutputByte(0x80);
            esDecoderOut.OutputByte(0x80);*/
            esDecoderOut.OutputByte(AACHeader.Num());
            esDecoderOut.Serialize((LPVOID)AACHeader.Array(), AACHeader.Num());
        }


        List<BYTE> esDescriptor;
        BufferOutputSerializer esOut(esDescriptor);
        esOut.OutputWord(0); //es id
        esOut.OutputByte(0); //stream priority
        esOut.OutputByte(4); //descriptor type
        /*esOut.OutputByte(0x80); //some stuff that no one should probably care about
        esOut.OutputByte(0x80);
        esOut.OutputByte(0x80);*/
        esOut.OutputByte(esDecoderDescriptor.Num());
        esOut.Serialize((LPVOID)esDecoderDescriptor.Array(), esDecoderDescriptor.Num());
        esOut.OutputByte(0x6);  //config descriptor type
        /*esOut.OutputByte(0x80); //some stuff that no one should probably care about
        esOut.OutputByte(0x80);
        esOut.OutputByte(0x80);*/
        esOut.OutputByte(1); //len
        esOut.OutputByte(2); //SL value(? always 2)

        //-------------------------------------------

        PushBox(output, DWORD_BE('moov'));

          //------------------------------------------------------
          // header
          PushBox(output, DWORD_BE('mvhd'));
            output.OutputDword(0); //version and flags (none)
            output.OutputDword(macTime); //creation time
            output.OutputDword(macTime); //modified time
            output.OutputDword(DWORD_BE(1000)); //time base (milliseconds, so 1000)
            output.OutputDword(videoDuration); //duration (in time base units)
            output.OutputDword(DWORD_BE(0x00010000)); //fixed point playback speed 1.0
            output.OutputWord(WORD_BE(0x0100)); //fixed point vol 1.0
            output.OutputQword(0); //reserved (10 bytes)
            output.OutputWord(0);
            output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
            output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
            output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
            output.OutputDword(0); //prevew start time (time base units)
            output.OutputDword(0); //prevew duration (time base units)
            output.OutputDword(0); //still poster frame (timestamp of frame)
            output.OutputDword(0); //selection(?) start time (time base units)
            output.OutputDword(0); //selection(?) duration (time base units)
            output.OutputDword(0); //current time (0, time base units)
            output.OutputDword(DWORD_BE(3)); //next free track id (1-based rather than 0-based)
          PopBox(output); //mvhd

          //------------------------------------------------------
          // audio track
          PushBox(output, DWORD_BE('trak'));
            PushBox(output, DWORD_BE('tkhd')); //track header
              output.OutputDword(DWORD_BE(0x00000007)); //version (0) and flags (0xF)
              output.OutputDword(macTime); //creation time
              output.OutputDword(macTime); //modified time
              output.OutputDword(DWORD_BE(1)); //track ID
              output.OutputDword(0); //reserved
              output.OutputDword(audioDuration); //duration (in time base units)
              output.OutputQword(0); //reserved
              output.OutputWord(0); //video layer (0)
              output.OutputWord(WORD_BE(0)); //quicktime alternate track id
              output.OutputWord(WORD_BE(0x0100)); //volume
              output.OutputWord(0); //reserved
              output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
              output.OutputDword(0); //width (fixed point)
              output.OutputDword(0); //height (fixed point)
            PopBox(output); //tkhd
            /*PushBox(output, DWORD_BE('edts'));
              PushBox(output, DWORD_BE('elst'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(DWORD_BE(1)); //count
                output.OutputDword(audioDuration); //duration
                output.OutputDword(0); //start time
                output.OutputDword(DWORD_BE(0x00010000)); //playback speed (1.0)
              PopBox(); //elst
            PopBox(); //tdst*/
            PushBox(output, DWORD_BE('mdia'));
              PushBox(output, DWORD_BE('mdhd'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(macTime); //creation time
                output.OutputDword(macTime); //modified time
                output.OutputDword(DWORD_BE(App->GetSampleRateHz())); //time scale
                output.OutputDword(audioUnitDuration);
                output.OutputDword(bMP3 ? DWORD_BE(0x55c40000) : DWORD_BE(0x15c70000));
              PopBox(output); //mdhd
              PushBox(output, DWORD_BE('hdlr'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(0); //quicktime type (none)
                output.OutputDword(DWORD_BE('soun')); //media type
                output.OutputDword(0); //manufacturer reserved
                output.OutputDword(0); //quicktime component reserved flags
                output.OutputDword(0); //quicktime component reserved mask
                output.Serialize((LPVOID)lpAudioTrack, (DWORD)strlen(lpAudioTrack)+1); //track name
              PopBox(output); //hdlr
              PushBox(output, DWORD_BE('minf'));
                PushBox(output, DWORD_BE('smhd'));
                  output.OutputDword(0); //version and flags (none)
                  output.OutputDword(0); //balance (fixed point)
                PopBox(output); //vdhd
                PushBox(output, DWORD_BE('dinf'));
                  PushBox(output, DWORD_BE('dref'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('url '));
                      output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
                    PopBox(output); //url
                  PopBox(output); //dref
                PopBox(output); //dinf
                PushBox(output, DWORD_BE('stbl'));
                  PushBox(output, DWORD_BE('stsd'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('mp4a'));
                      output.OutputDword(0); //reserved (6 bytes)
                      output.OutputWord(0);
                      output.OutputWord(WORD_BE(1)); //dref index
                      output.OutputWord(0); //quicktime encoding version
                      output.OutputWord(0); //quicktime encoding revision
                      output.OutputDword(0); //quicktime audio encoding vendor
                      output.OutputWord(0); //channels (ignored)
                      output.OutputWord(WORD_BE(16)); //sample size
                      output.OutputWord(0); //quicktime audio compression id
                      output.OutputWord(0); //quicktime audio packet size
                      output.OutputDword(DWORD_BE(App->GetSampleRateHz()<<16)); //sample rate (fixed point)
                      PushBox(output, DWORD_BE('esds'));
                        output.OutputDword(0); //version and flags (none)
                        output.OutputByte(3); //ES descriptor type
                        /*output.OutputByte(0x80);
                        output.OutputByte(0x80);
                        output.OutputByte(0x80);*/
                        output.OutputByte(esDescriptor.Num());
                        output.Serialize((LPVOID)esDescriptor.Array(), esDescriptor.Num());
                      PopBox(output);
                    PopBox(output);
                  PopBox(output); //stsd
                  PushBox(output, DWORD_BE('stts')); //list of keyframe (i-frame) IDs
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(audioDecodeTimes.Num()));
                    for(UINT i=0; i<audioDecodeTimes.Num(); i++)
                    {
                        output.OutputDword(fastHtonl(audioDecodeTimes[i].count));
                        output.OutputDword(fastHtonl(audioDecodeTimes[i].val));
                    }
                  PopBox(output); //stss
                  PushBox(output, DWORD_BE('stsc')); //sample to chunk list
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(audioSampleToChunk.Num()));
                    for(UINT i=0; i<audioSampleToChunk.Num(); i++)
                    {
                        SampleToChunk &stc  = audioSampleToChunk[i];
                        output.OutputDword(fastHtonl(stc.firstChunkID));
                        output.OutputDword(fastHtonl(stc.samplesPerChunk));
                        output.OutputDword(DWORD_BE(1));
                    }
                  PopBox(output); //stsc

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 30, 0);
                  //ProcessEvents();

                  PushBox(output, DWORD_BE('stsz')); //sample sizes
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(0); //block size for all (0 if differing sizes)
                    output.OutputDword(fastHtonl(audioFrames.Num()));
                    for(UINT i=0; i<audioFrames.Num(); i++)
                        output.OutputDword(fastHtonl(audioFrames[i].size));
                  PopBox(output);

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 40, 0);
                  //ProcessEvents();

                  if(audioChunks.Num() && audioChunks.Last() > 0xFFFFFFFFLL)
                  {
                      PushBox(output, DWORD_BE('co64')); //chunk offsets
                      output.OutputDword(0); //version and flags (none)
                      output.OutputDword(fastHtonl(audioChunks.Num()));
                      for(UINT i=0; i<audioChunks.Num(); i++)
                          output.OutputQword(fastHtonll(audioChunks[i]));
                      PopBox(output); //co64
                  }
                  else
                  {
                      PushBox(output, DWORD_BE('stco')); //chunk offsets
                        output.OutputDword(0); //version and flags (none)
                        output.OutputDword(fastHtonl(audioChunks.Num()));
                        for(UINT i=0; i<audioChunks.Num(); i++)
                            output.OutputDword(fastHtonl((DWORD)audioChunks[i]));
                      PopBox(output); //stco
                  }
                PopBox(output); //stbl
              PopBox(output); //minf
            PopBox(output); //mdia
          PopBox(output); //trak

          //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 50, 0);
          //ProcessEvents();

          //------------------------------------------------------
          // video track
          PushBox(output, DWORD_BE('trak'));
            PushBox(output, DWORD_BE('tkhd')); //track header
              output.OutputDword(DWORD_BE(0x00000007)); //version (0) and flags (0x7)
              output.OutputDword(macTime); //creation time
              output.OutputDword(macTime); //modified time
              output.OutputDword(DWORD_BE(2)); //track ID
              output.OutputDword(0); //reserved
              output.OutputDword(videoDuration); //duration (in time base units)
              output.OutputQword(0); //reserved
              output.OutputWord(0); //video layer (0)
              output.OutputWord(0); //quicktime alternate track id (0)
              output.OutputWord(0); //track audio volume (this is video, so 0)
              output.OutputWord(0); //reserved
              output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
              output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
              output.OutputDword(fastHtonl(width<<16));  //width (fixed point)
              output.OutputDword(fastHtonl(height<<16)); //height (fixed point)
            PopBox(output); //tkhd
            /*PushBox(output, DWORD_BE('edts'));
              PushBox(output, DWORD_BE('elst'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(DWORD_BE(1)); //count
                output.OutputDword(videoDuration); //duration
                output.OutputDword(0); //start time
                output.OutputDword(DWORD_BE(0x00010000)); //playback speed (1.0)
              PopBox(); //elst
            PopBox(); //tdst*/
            PushBox(output, DWORD_BE('mdia'));
              PushBox(output, DWORD_BE('mdhd'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(macTime); //creation time
                output.OutputDword(macTime); //modified time
                output.OutputDword(DWORD_BE(1000)); //time scale
                output.OutputDword(videoDuration);
                output.OutputDword(DWORD_BE(0x55c40000));
              PopBox(output); //mdhd
              PushBox(output, DWORD_BE('hdlr'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(0); //quicktime type (none)
                output.OutputDword(DWORD_BE('vide')); //media type
                output.OutputDword(0); //manufacturer reserved
                output.OutputDword(0); //quicktime component reserved flags
                output.OutputDword(0); //quicktime component reserved mask
                output.Serialize((LPVOID)lpVideoTrack, (DWORD)strlen(lpVideoTrack)+1); //track name
              PopBox(output); //hdlr
              PushBox(output, DWORD_BE('minf'));
                PushBox(output, DWORD_BE('vmhd'));
                  output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
                  output.OutputWord(0); //quickdraw graphic mode (copy = 0)
                  output.OutputWord(0); //quickdraw red value
                  output.OutputWord(0); //quickdraw green value
                  output.OutputWord(0); //quickdraw blue value
                PopBox(output); //vdhd
                PushBox(output, DWORD_BE('dinf'));
                  PushBox(output, DWORD_BE('dref'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('url '));
                      output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
                    PopBox(output); //url
                  PopBox(output); //dref
                PopBox(output); //dinf
                PushBox(output, DWORD_BE('stbl'));
                  PushBox(output, DWORD_BE('stsd'));
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(DWORD_BE(1)); //count
                    PushBox(output, DWORD_BE('avc1'));
                      output.OutputDword(0); //reserved 6 bytes
                      output.OutputWord(0);
                      output.OutputWord(WORD_BE(1)); //index
                      output.OutputWord(0); //encoding version
                      output.OutputWord(0); //encoding revision level
                      output.OutputDword(0); //encoding vendor
                      output.OutputDword(0); //temporal quality
                      output.OutputDword(0); //spatial quality
                      output.OutputWord(fastHtons(width)); //width
                      output.OutputWord(fastHtons(height)); //height
                      output.OutputDword(DWORD_BE(0x00480000)); //fixed point width pixel resolution (72.0)
                      output.OutputDword(DWORD_BE(0x00480000)); //fixed point height pixel resolution (72.0)
                      output.OutputDword(0); //quicktime video data size 
                      output.OutputWord(WORD_BE(1)); //frame count(?)
                      output.OutputByte((BYTE)strlen(videoCompressionName)); //compression name length
                      output.Serialize(videoCompressionName, 31); //31 bytes for the name
                      output.OutputWord(WORD_BE(24)); //bit depth
                      output.OutputWord(0xFFFF); //quicktime video color table id (none = -1)
                      PushBox(output, DWORD_BE('avcC'));
                        output.OutputByte(1); //version
                        output.OutputByte(100); //h264 profile ID
                        output.OutputByte(0); //h264 compatible profiles
                        output.OutputByte(0x1f); //h264 level
                        output.OutputByte(0xff); //reserved
                        output.OutputByte(0xe1); //first half-byte = no clue. second half = sps count
                        output.OutputWord(fastHtons(SPS.Num())); //sps size
                        output.Serialize(SPS.Array(), SPS.Num()); //sps data
                        output.OutputByte(1); //pps count
                        output.OutputWord(fastHtons(PPS.Num())); //pps size
                        output.Serialize(PPS.Array(), PPS.Num()); //pps data
                      PopBox(output); //avcC
                    PopBox(output); //avc1
                  PopBox(output); //stsd
                  PushBox(output, DWORD_BE('stts')); //frame times
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(videoDecodeTimes.Num()));
                    for(UINT i=0; i<videoDecodeTimes.Num(); i++)
                    {
                        output.OutputDword(fastHtonl(videoDecodeTimes[i].count));
                        output.OutputDword(fastHtonl(videoDecodeTimes[i].val));
                    }
                  PopBox(output); //stts

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 60, 0);
                  //ProcessEvents();

                  if (IFrameIDs.Num())
                  {
                      PushBox(output, DWORD_BE('stss')); //list of keyframe (i-frame) IDs
                        output.OutputDword(0); //version and flags (none)
                        output.OutputDword(fastHtonl(IFrameIDs.Num()));
                        output.Serialize(IFrameIDs.Array(), IFrameIDs.Num()*sizeof(UINT));
                      PopBox(output); //stss
                  }
                  PushBox(output, DWORD_BE('ctts')); //list of composition time offsets
                    output.OutputDword(0); //version (0) and flags (none)
                    //output.OutputDword(DWORD_BE(0x01000000)); //version (1) and flags (none)

                    output.OutputDword(fastHtonl(compositionOffsets.Num()));
                    for(UINT i=0; i<compositionOffsets.Num(); i++)
                    {
                        output.OutputDword(fastHtonl(compositionOffsets[i].count));
                        output.OutputDword(fastHtonl(compositionOffsets[i].val));
                    }
                  PopBox(output); //ctts

                  //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 70, 0);
                  //ProcessEvents();

                  PushBox(output, DWORD_BE('stsc')); //sample to chunk list
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(fastHtonl(videoSampleToChunk.Num()));
                    for(UINT i=0; i<videoSampleToChunk.Num(); i++)
                    {
                        SampleToChunk &stc  = videoSampleToChunk[i];
                        output.OutputDword(fastHtonl(stc.firstChunkID));
                        output.OutputDword(fastHtonl(stc.samplesPerChunk));
                        output.OutputDword(DWORD_BE(1));
                    }
                  PopBox(output); //stsc
                  PushBox(output, DWORD_BE('stsz')); //sample sizes
                    output.OutputDword(0); //version and flags (none)
                    output.OutputDword(0); //block size for all (0 if differing sizes)
                    output.OutputDword(fastHtonl(videoFrames.Num()));
                    for(UINT i=0; i<videoFrames.Num(); i++)
                        output.OutputDword(fastHtonl(videoFrames[i].size));
                  PopBox(output);

                  if(videoChunks.Num() && videoChunks.Last() > 0xFFFFFFFFLL)
                  {
                      PushBox(output, DWORD_BE('co64')); //chunk offsets
                      output.OutputDword(0); //version and flags (none)
                      output.OutputDword(fastHtonl(videoChunks.Num()));
                      for(UINT i=0; i<videoChunks.Num(); i++)
                          output.OutputQword(fastHtonll(videoChunks[i]));
                      PopBox(output); //co64
                  }
                  else
                  {
                      PushBox(output, DWORD_BE('stco')); //chunk offsets
                        output.OutputDword(0); //version and flags (none)
                        output.OutputDword(fastHtonl(videoChunks.Num()));
                        for(UINT i=0; i<videoChunks.Num(); i++)
                            output.OutputDword(fastHtonl((DWORD)videoChunks[i]));
                      PopBox(output); //stco
                  }
                PopBox(output); //stbl
              PopBox(output); //minf
            PopBox(output); //mdia
          PopBox(output); //trak

          //SendMessage(GetDlgItem(hwndProgressDialog, IDC_PROGRESS1), PBM_SETPOS, 80, 0);
          //ProcessEvents();

          //------------------------------------------------------
          // info thingy
          PushBox(output, DWORD_BE('udta'));
            PushBox(output, DWORD_BE('meta'));
              output.OutputDword(0); //version and flags (none)
              PushBox(output, DWORD_BE('hdlr'));
                output.OutputDword(0); //version and flags (none)
                output.OutputDword(0); //quicktime type
                output.OutputDword(DWORD_BE('mdir')); //metadata type
                output.OutputDword(DWORD_BE('appl')); //quicktime manufacturer reserved thingy
                output.OutputDword(0); //quicktime component reserved flag
                output.OutputDword(0); //quicktime component reserved flag mask
                output.OutputByte(0); //null string
              PopBox(output); //hdlr
              PushBox(output, DWORD_BE('ilst'));
                PushBox(output, DWORD_BE('\xa9too'));
                  PushBox(output, DWORD_BE('data'));
                    output.OutputDword(DWORD_BE(1)); //version (1) + flags (0)
                    output.OutputDword(0); //reserved
                    LPSTR lpVersion = OBS_VERSION_STRING_ANSI;
                    output.Serialize(lpVersion, (DWORD)strlen(lpVersion));
                  PopBox(output); //data
                PopBox(output); //@too
              PopBox(output); //ilst
            PopBox(output); //meta
          PopBox(output); //udta

        PopBox(output); //moov

        fileOut.Serialize(endBuffer.Array(), (DWORD)output.GetPos());
        fileOut.Close();

        XFile file;
        if(file.Open(strFile, XFILE_WRITE, XFILE_OPENEXISTING))
        {
#ifdef USE_64BIT_MP4
            file.SetPos((INT64)mdatStart+8, XFILE_BEGIN);

            UINT64 size = fastHtonll(mdatStop-mdatStart);
            file.Write(&size, 8);
#else
            file.SetPos((INT64)mdatStart, XFILE_BEGIN);
            UINT size = fastHtonl((DWORD)(mdatStop-mdatStart));
            file.Write(&size, 4);
#endif
            file.Close();
        }

        App->EnableSceneSwitching(true);

        //DestroyWindow(hwndProgressDialog);
    }
Example #3
0
void SkinMesh::LoadAnimations(CTSTR lpName)
{
    traceIn(SkinMesh::LoadAnimations);

    assert(lpName);

    DWORD i, j, k;

    String strMeshPath;
    if(!Engine::ConvertResourceName(lpName, TEXT("models"), strMeshPath))
        return;

    String strFilePath;
    strFilePath << GetPathWithoutExtension(strMeshPath) << TEXT(".xan");


    //-----------------------------------------------

    XFileInputSerializer animData;

    if(!animData.Open(strFilePath))
    {
        AppWarning(TEXT("Could not open animation file \"%s\""), (TSTR)strFilePath);
        return;
    }

    DWORD ver;
    animData << ver;

    if(ver != ANIMFILE_VER && ver != 0x100)
    {
        AppWarning(TEXT("'%s': Bad animation file version"), strFilePath);
        animData.Close();
        return;
    }

    //-----------------------------------------------

    unsigned int numSequences;
    animData << numSequences;
    SequenceList.SetSize(numSequences);
    for(i=0; i<numSequences; i++)
        animData << SequenceList[i];

    DWORD nBones;
    animData << nBones;
    BoneList.SetSize(nBones);
    for(i=0; i<nBones; i++)
    {
        Bone &bone = BoneList[i];

        List<DWORD>     RigidVerts;
        List<VWeight>   BlendedVerts;
        DWORD           nRigid, nBlended;

        //----------------------------

        animData << bone.Pos
                 << bone.Rot
                 << nRigid
                 << nBlended
                 << bone.flags
                 << bone.idParent;

        animData << RigidVerts;
        animData << BlendedVerts;

        if(bone.idParent != 0xFFFFFFFF)
        {
            bone.Parent = BoneList+bone.idParent;
            bone.LocalPos = bone.Pos - BoneList[bone.idParent].Pos;
            BoneList[bone.idParent].Children << &bone;
        }
        else
        {
            bone.Parent = NULL;
            bone.LocalPos = bone.Pos;

            bone.flags |= BONE_ROOT;
        }

        //----------------------------

        bone.Weights.SetSize(nRigid);

        for(j=0; j<RigidVerts.Num(); j++)
        {
            VWeight &weight = bone.Weights[j];

            weight.vert = RigidVerts[j];
            weight.weight = 1.0f;
        }

        bone.Weights.AppendList(BlendedVerts);

        //----------------------------

        DWORD nAnim;
        animData << nAnim;
        bone.seqKeys.SetSize(nAnim);
        for(j=0; j<nAnim; j++)
        {
            SeqKeys &keys = bone.seqKeys[j];

            DWORD nPosKeys, nRotKeys;

            //----------------------------
            
            animData << nRotKeys;
            keys.hasRotKeys = nRotKeys > 0;
            if(keys.hasRotKeys)
            {
                keys.lpRotKeys = (Quat*)Allocate(nRotKeys*sizeof(Quat));
                animData.Serialize(keys.lpRotKeys, nRotKeys*sizeof(Quat));

                keys.lpRotTans = (Quat*)Allocate(nRotKeys*sizeof(Quat));
                for(k=0; k<nRotKeys; k++)
                {
                    DWORD kp1 = (k == (nRotKeys-1)) ? k : (k+1);
                    DWORD km1 = (k == 0) ? 0 : k-1;

                    Quat &qk   = keys.lpRotKeys[k];
                    Quat &qkp1 = keys.lpRotKeys[kp1];
                    Quat &qkm1 = keys.lpRotKeys[km1];

                    keys.lpRotTans[k] = qk.GetInterpolationTangent(qkm1, qkp1);
                }
            }

            //----------------------------
            
            animData << nPosKeys;
            keys.hasPosKeys = nPosKeys > 0;
            if(keys.hasPosKeys)
            {
                keys.lpPosKeys = (Vect*)Allocate(nPosKeys*sizeof(Vect));
                Vect::SerializeArray(animData, keys.lpPosKeys, nPosKeys);
                
                keys.lpPosTans = (Vect*)Allocate(nPosKeys*sizeof(Vect));
                for(k=0; k<nPosKeys; k++)
                {
                    DWORD kp1 = (k == (nPosKeys-1)) ? k : (k+1);
                    DWORD km1 = (k == 0) ? 0 : k-1;

                    Vect &pk   = keys.lpPosKeys[k];
                    Vect &pkp1 = keys.lpPosKeys[kp1];
                    Vect &pkm1 = keys.lpPosKeys[km1];

                    keys.lpPosTans[k] = pk.GetInterpolationTangent(pkm1, pkp1);
                }
            }
        }
    }

    //-----------------------------------------------

    int num;
    animData << num;
    BoneExtensions.SetSize(num);
    BoneExtensionNames.SetSize(num);

    for(int i=0; i<num; i++)
    {
        animData << BoneExtensionNames[i];
        animData << BoneExtensions[i];
    }

    //-----------------------------------------------

    AnimatedSections.SetSize(nSections);

    if(ver == 0x100) //remove
    {
        UINT *indices = (UINT*)IdxBuffer->GetData();

        List<UINT> adjustedIndices;
        adjustedIndices.CopyArray(indices, IdxBuffer->NumIndices());

        VBData *vbd = VertBuffer->GetData();
        List<Vect> &verts = vbd->VertList;

        //--------- 
        // get vert data
        List<VertAnimInfo> vertInfo;
        vertInfo.SetSize(nVerts);

        for(int i=0; i<nBones; i++)
        {
            Bone &bone = BoneList[i];
            for(int j=0; j<bone.Weights.Num(); j++)
            {
                VWeight &vertWeight = bone.Weights[j];

                if(!vertInfo[vertWeight.vert].bones.HasValue(i))
                {
                    vertInfo[vertWeight.vert].bones << i;
                    vertInfo[vertWeight.vert].weights << vertWeight.weight;
                }
            }
        }

        //--------- 
        // remove excess bone influence from verts (let just set the max to 3 for this)
        /*for(int i=0; i<vertInfo.Num(); i++)
        {
            VertAnimInfo &vert = vertInfo[i];

            while(vert.bones.Num() > 3)
            {
                float weakestWeight = M_INFINITE;
                UINT weakestID;

                for(int j=0; j<vert.bones.Num(); j++)
                {
                    if(vert.weights[j] < weakestWeight)
                    {
                        weakestID = j;
                        weakestWeight = vert.weights[j];
                    }
                }

                float weightAdjust = 1.0f/(1.0f-weakestWeight);
                vert.weights.Remove(weakestID);
                vert.bones.Remove(weakestID);

                for(int j=0; j<vert.weights.Num(); j++)
                    vert.weights[j] *= weightAdjust;
            }
        }*/
        for(int i=0; i<vertInfo.Num(); i++)
        {
            VertAnimInfo &vert = vertInfo[i];

            for(int j=0; j<vert.bones.Num(); j++)
            {
                if(vert.weights[j] <= 0.15f)
                {
                    float weightAdjust = 1.0f/(1.0f-vert.weights[j]);
                    vert.weights.Remove(j);
                    vert.bones.Remove(j);

                    for(int k=0; k<vert.weights.Num(); k++)
                        vert.weights[k] *= weightAdjust;

                    --j;
                }
            }
        }

        //--------- 
        // remove excess bone influence from tris (can only have 4 bones influencing any triangle)
        for(int i=0; i<nSections; i++)
        {
            DrawSection &section = SectionList[i];
            if(!section.numFaces) continue;

            for(int j=0; j<section.numFaces; j++)
            {
                UINT *triVertIDs = &indices[(section.startFace+j)*3];

                List<UINT> bones;
                List<float> bestVertWeights;

                for(int k=0; k<3; k++)
                {
                    VertAnimInfo &info = vertInfo[triVertIDs[k]];

                    for(int l=0; l<info.bones.Num(); l++)
                    {
                        UINT id = bones.FindValueIndex(info.bones[l]);
                        if(id == INVALID)
                        {
                            bones.Add(info.bones[l]);
                            bestVertWeights.Add(info.weights[l]);
                        }
                        else
                            bestVertWeights[id] = MAX(bestVertWeights[id], info.weights[l]);
                    }
                }

                while(bones.Num() > 4)
                {
                    int removeBone, removeBoneID;
                    float bestWeight = M_INFINITE;

                    for(int k=0; k<bones.Num(); k++)
                    {
                        if(bestVertWeights[k] < bestWeight)
                        {
                            removeBone = bones[k];
                            removeBoneID = k;
                            bestWeight = bestVertWeights[k];
                        }
                    }

                    for(int k=0; k<3; k++)
                    {
                        VertAnimInfo &info = vertInfo[triVertIDs[k]];
                        UINT id = info.bones.FindValueIndex(removeBone);
                        if(id == INVALID) continue;

                        float weightAdjust = 1.0f/(1.0f-info.weights[id]);
                        info.weights.Remove(id);
                        info.bones.Remove(id);

                        for(int l=0; l<info.weights.Num(); l++)
                            info.weights[l] *= weightAdjust;
                    }

                    bones.Remove(removeBoneID);
                    bestVertWeights.Remove(removeBoneID);
                }
            }
        }

        //--------- 
        // sort out sections of triangles that are influenced up to a max of 4 bones
        // also, duplicate shared verts
        VBData *newVBD = new VBData;
        newVBD->CopyList(*vbd);

        newVBD->TVList.SetSize(2);
        newVBD->TVList[1].SetWidth(4);
        newVBD->TVList[1].SetSize(nVerts);

        List<SubSectionInfo> newSubSections;

        for(int i=0; i<nSections; i++)
        {
            List<TriBoneInfo> triInfo;

            DrawSection &section = SectionList[i];
            if(!section.numFaces) continue;

            for(int j=0; j<section.numFaces; j++)
            {
                UINT *triVertIDs = &indices[(section.startFace+j)*3];

                TriBoneInfo &newTri = *triInfo.CreateNew();

                for(int k=0; k<3; k++)
                {
                    VertAnimInfo &info = vertInfo[triVertIDs[k]];

                    for(int l=0; l<info.bones.Num(); l++)
                        newTri.bones.SafeAdd(info.bones[l]);
                }
            }

            BitList UsedTris;
            UsedTris.SetSize(section.numFaces);
            DWORD nUsedTris = 0;

            while(nUsedTris != section.numFaces)
            {
                DWORD triSectionID = newSubSections.Num();
                SubSectionInfo *curSubSecInfo = newSubSections.CreateNew();
                curSubSecInfo->section = i;

                for(int j=0; j<triInfo.Num(); j++)
                {
                    if(UsedTris[j]) continue;

                    TriBoneInfo &tri = triInfo[j];

                    List<UINT> secBones;
                    secBones.CopyList(curSubSecInfo->bones);

                    BOOL bBadTri = FALSE;
                    for(int k=0; k<tri.bones.Num(); k++)
                    {
                        secBones.SafeAdd(tri.bones[k]);
                        if(secBones.Num() > 4)
                            break;
                    }

                    if(secBones.Num() > 4) continue;

                    DWORD triID = section.startFace+j;

                    curSubSecInfo->bones.CopyList(secBones);
                    curSubSecInfo->tris << triID;

                    UINT *triVertIDs = &indices[triID*3];
                    for(int k=0; k<3; k++)
                    {
                        VertAnimInfo *info = &vertInfo[triVertIDs[k]];
                        UINT id = info->sections.FindValueIndex(triSectionID);
                        if(id == INVALID)
                        {
                            UINT vertID;
                            if(info->sections.Num() >= 1) //duplicate vertex
                            {
                                vertID = newVBD->DuplicateVertex(triVertIDs[k]);
                                adjustedIndices[(triID*3)+k] = vertID;

                                VertAnimInfo &newVertInfo = *vertInfo.CreateNew();
                                info = &vertInfo[triVertIDs[k]]; //reset pointer

                                newVertInfo.bones.CopyList(info->bones);
                                newVertInfo.weights.CopyList(info->weights);
                                newVertInfo.sections << triSectionID;
                            }
                            else
                                vertID = triVertIDs[k];

                            info->sections << triSectionID;
                            info->sectionVertIDs << vertID;

                            List<Vect4> &vertWeights = *newVBD->TVList[1].GetV4();
                            for(int l=0; l<4; l++)
                            {
                                if(l >= curSubSecInfo->bones.Num())
                                    vertWeights[vertID].ptr[l] = 0.0f;
                                else
                                {
                                    UINT boneID = curSubSecInfo->bones[l];
                                    UINT weightID = info->bones.FindValueIndex(boneID);
                                    if(weightID == INVALID)
                                        vertWeights[vertID].ptr[l] = 0.0f;
                                    else
                                        vertWeights[vertID].ptr[l] = info->weights[weightID];
                                }
                            }
                        }
                        else
                            adjustedIndices[(triID*3)+k] = info->sectionVertIDs[id];
                    }

                    UsedTris.Set(j);
                    ++nUsedTris;
                }
            }
            for(int j=0; j<triInfo.Num(); j++)
                triInfo[j].FreeData();
        }

        //---------
        // create animated draw sections and create new index buffer
        DWORD curTriID = 0;
        List<UINT> newIndices;

        for(int i=0; i<newSubSections.Num(); i++)
        {
            SubSectionInfo &subSecInfo = newSubSections[i];
            AnimSection &animSection = AnimatedSections[subSecInfo.section];
            AnimSubSection &subSection = *animSection.SubSections.CreateNew();

            subSection.numBones = subSecInfo.bones.Num();
            for(int j=0; j<subSecInfo.bones.Num(); j++)
                subSection.bones[j] = subSecInfo.bones[j];

            subSection.startFace = curTriID;

            for(int j=0; j<subSecInfo.tris.Num(); j++)
            {
                UINT *tri = &adjustedIndices[subSecInfo.tris[j]*3];
                newIndices << tri[0] << tri[1] << tri[2];

                ++curTriID;
            }

            subSection.numFaces = curTriID-subSection.startFace;

            subSecInfo.FreeData();
        }

        //rebuild original bone data
        for(int i=0; i<BoneList.Num(); i++)
            BoneList[i].Weights.Clear();

        for(int i=0; i<vertInfo.Num(); i++)
        {
            for(int j=0; j<vertInfo[i].bones.Num(); j++)
            {
                VWeight weight;
                weight.vert = i;
                weight.weight = vertInfo[i].weights[j];

                BoneList[vertInfo[i].bones[j]].Weights << weight;
            }

            vertInfo[i].FreeData();
        }

        delete VertBuffer;
        delete IdxBuffer;

        UINT numIndices;
        UINT *indexArray;
        newIndices.TransferTo(indexArray, numIndices);

        nVerts = newVBD->VertList.Num();

        IdxBuffer = CreateIndexBuffer(GS_UNSIGNED_LONG, indexArray, numIndices);
        VertBuffer = CreateVertexBuffer(newVBD);
    }
    else
    {
        for(int i=0; i<nSections; i++)
            animData << AnimatedSections[i].SubSections;
    }

    animData.Close();

    traceOut;
}
Example #4
0
void OBS::MainAudioLoop()
{
    DWORD taskID = 0;
    HANDLE hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskID);

    bPushToTalkOn = false;

    micMax = desktopMax = VOL_MIN;
    micPeak = desktopPeak = VOL_MIN;

    UINT audioFramesSinceMeterUpdate = 0;
    UINT audioFramesSinceMicMaxUpdate = 0;
    UINT audioFramesSinceDesktopMaxUpdate = 0;

    List<float> mixedLatestDesktopSamples;

    List<float> blank10msSample;
    blank10msSample.SetSize(882);

    QWORD lastAudioTime = 0;

    while(TRUE)
    {
        OSSleep(5); //screw it, just run it every 5ms

        if(!bRunning)
            break;

        //-----------------------------------------------

        float *desktopBuffer, *micBuffer;
        UINT desktopAudioFrames = 0, micAudioFrames = 0;
        UINT latestDesktopAudioFrames = 0, latestMicAudioFrames = 0;

        curDesktopVol = desktopVol * desktopBoost;

        if(bUsingPushToTalk)
            curMicVol = bPushToTalkOn ? micVol : 0.0f;
        else
            curMicVol = micVol;

        curMicVol *= micBoost;

        bool bDesktopMuted = (curDesktopVol < EPSILON);
        bool bMicEnabled   = (micAudio != NULL);

        QWORD timestamp;
        while(QueryNewAudio(timestamp))
        {
            if (!lastAudioTime)
                lastAudioTime = App->GetSceneTimestamp();

            if (lastAudioTime < timestamp) {
                while ((lastAudioTime+=10) < timestamp)
                    EncodeAudioSegment(blank10msSample.Array(), 441, lastAudioTime);
            }

            //----------------------------------------------------------------------------
            // get latest sample for calculating the volume levels

            float *latestDesktopBuffer = NULL, *latestMicBuffer = NULL;

            desktopAudio->GetBuffer(&desktopBuffer, &desktopAudioFrames, timestamp-10);
            desktopAudio->GetNewestFrame(&latestDesktopBuffer, &latestDesktopAudioFrames);

            UINT totalFloats = desktopAudioFrames*2;
            if(bDesktopMuted)
            {
                // Clearing the desktop audio buffer before mixing in the auxiliary audio sources.
                zero(desktopBuffer, sizeof(*desktopBuffer)*totalFloats);
            }

            if(micAudio != NULL)
            {
                micAudio->GetBuffer(&micBuffer, &micAudioFrames, timestamp-10);
                micAudio->GetNewestFrame(&latestMicBuffer, &latestMicAudioFrames);
            }

            //----------------------------------------------------------------------------
            // get latest aux volume level samples and mix

            OSEnterMutex(hAuxAudioMutex);

            mixedLatestDesktopSamples.CopyArray(latestDesktopBuffer, latestDesktopAudioFrames*2);
            for(UINT i=0; i<auxAudioSources.Num(); i++)
            {
                float *latestAuxBuffer;

                if(auxAudioSources[i]->GetNewestFrame(&latestAuxBuffer, &latestDesktopAudioFrames))
                    MixAudio(mixedLatestDesktopSamples.Array(), latestAuxBuffer, latestDesktopAudioFrames*2, false);
            }

            //----------------------------------------------------------------------------
            // mix output aux sound samples with the desktop

            for(UINT i=0; i<auxAudioSources.Num(); i++)
            {
                float *auxBuffer;

                if(auxAudioSources[i]->GetBuffer(&auxBuffer, &desktopAudioFrames, timestamp-10))
                    MixAudio(desktopBuffer, auxBuffer, desktopAudioFrames*2, false);
            }

            OSLeaveMutex(hAuxAudioMutex);

            //----------------------------------------------------------------------------

            //UINT totalFloats = desktopAudioFrames*2;

            //----------------------------------------------------------------------------

            /*multiply samples by volume and compute RMS and max of samples*/
            float desktopRMS = 0, micRMS = 0, desktopMx = 0, micMx = 0;
            // Use 1.0f instead of curDesktopVol, since aux audio sources already have their volume set, and shouldn't be boosted anyway.
            if(latestDesktopBuffer)
                CalculateVolumeLevels(mixedLatestDesktopSamples.Array(), latestDesktopAudioFrames*2, 1.0f, desktopRMS, desktopMx);
            if(bMicEnabled && latestMicBuffer)
                CalculateVolumeLevels(latestMicBuffer, latestMicAudioFrames*2, curMicVol, micRMS, micMx);

            /*convert RMS and Max of samples to dB*/            
            desktopRMS = toDB(desktopRMS);
            micRMS = toDB(micRMS);
            desktopMx = toDB(desktopMx);
            micMx = toDB(micMx);

            /* update max if sample max is greater or after 1 second */
            float maxAlpha = 0.15f;
            UINT peakMeterDelayFrames = 44100 * 3;
            if(micMx > micMax)
            {
                micMax = micMx;
            }
            else
            {
                micMax = maxAlpha * micMx + (1.0f - maxAlpha) * micMax;
            }

            if(desktopMx > desktopMax)
            {
                desktopMax = desktopMx;
            }
            else
            {
                desktopMax = maxAlpha * desktopMx + (1.0f - maxAlpha) * desktopMax;
            }

            /*update delayed peak meter*/
            if(micMax > micPeak || audioFramesSinceMicMaxUpdate > peakMeterDelayFrames)
            {
                micPeak = micMax;
                audioFramesSinceMicMaxUpdate = 0;
            }
            else
            {
                audioFramesSinceMicMaxUpdate += desktopAudioFrames;
            }

            if(desktopMax > desktopPeak || audioFramesSinceDesktopMaxUpdate > peakMeterDelayFrames)
            {
                desktopPeak = desktopMax;
                audioFramesSinceDesktopMaxUpdate = 0;
            }
            else
            {
                audioFramesSinceDesktopMaxUpdate += desktopAudioFrames;
            }

            /*low pass the level sampling*/
            float rmsAlpha = 0.15f;
            desktopMag = rmsAlpha * desktopRMS + desktopMag * (1.0f - rmsAlpha);
            micMag = rmsAlpha * micRMS + micMag * (1.0f - rmsAlpha);

            /*update the meter about every 50ms*/
            audioFramesSinceMeterUpdate += desktopAudioFrames;
            if(audioFramesSinceMeterUpdate >= 2205)
            {
                PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_MICVOLUMEMETER, VOLN_METERED), 0);
                audioFramesSinceMeterUpdate = 0;
            }

            //----------------------------------------------------------------------------
            // mix mic and desktop sound, using SSE2 if available
            // also, it's perfectly fine to just mix into the returned buffer

            if(bMicEnabled)
                MixAudio(desktopBuffer, micBuffer, totalFloats, bForceMicMono);

            EncodeAudioSegment(desktopBuffer, totalFloats>>1, lastAudioTime);
        }

        //-----------------------------------------------

        if(!bRecievedFirstAudioFrame && pendingAudioFrames.Num())
            bRecievedFirstAudioFrame = true;
    }

    desktopMag = desktopMax = desktopPeak = VOL_MIN;
    micMag = micMax = micPeak = VOL_MIN;

    PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_MICVOLUMEMETER, VOLN_METERED), 0);

    for(UINT i=0; i<pendingAudioFrames.Num(); i++)
        pendingAudioFrames[i].audioData.Clear();

    AvRevertMmThreadCharacteristics(hTask);
}