Split Input

Started by Couin,

Couin

Hi :)

Related with my previous topic, about streaming to icecast, I would also record the audio of input.
But I would start and stop streaming and recording separately. I mean, I don't want to stop recording to stop streaming, for example.

Here a schematic of what I would do:
stream_and_record.jpg
I would attrib volume to the input, and get level after setting volume.
Then, I would split to 2 channels, so one (RecordingChan) for recording to file and the other (StreamingChan), to stream to icecast server.
I would start and stop recording and streaming separately, with for example:
BASS_ChannelStart(RecordingChan) / BASS_ChannelStop(RecordingChan)
BASS_ChannelStart(StreamingChan) / BASS_ChannelStop(StreamingChan)


I tried a lot of methods, with mixers, splitters, (without attrib vol for the moment) but they still very confusus for me  :'(
I don't know exactly what to use, BASS_RECORD_PAUSE or not, when should I use BASS_STREAM_DECODE (if I have to use it)...

Some guidelines whold be welcome  :-*

Thanks :)



Couin

Hi,

I get some first results with this code:
    Call BASS_Init(0, 44100, 0, 0, 0)

    Mixer = BASS_Mixer_StreamCreate(44100, 2, BASS_SAMPLE_FLOAT Or BASS_STREAM_DECODE Or BASS_MIXER_NONSTOP)
    Call BASS_ChannelSetAttribute(Mixer, BASS_ATTRIB_BUFFER, 0)
   
    Call BASS_RecordInit(-1)
    rchan = BASS_RecordStart(44100, 2, 0, 0, 0)
       
    If (rchan = 0) Then
        Call Error_("Couldn't start recording")
        Exit Sub
    End If
   
    Call BASS_ChannelPlay(rchan, False)

    Call BASS_ChannelPlay(Mixer, False)
    Call BASS_Mixer_StreamAddChannel(Mixer, rchan, BASS_MIXER_CHAN_BUFFER)
      
    VusChan = BASS_Split_StreamCreate(Mixer, 0, ByVal 0)
    Call BASS_ChannelSetAttribute(VusChan, BASS_ATTRIB_BUFFER, 0)
    Call BASS_ChannelPlay(VusChan, False)
       
    RecChan = BASS_Split_StreamCreate(Mixer, 0, ByVal 0)
    Call BASS_ChannelSetAttribute(RecChan, BASS_ATTRIB_BUFFER, 0)
       
    StrChan = BASS_Split_StreamCreate(Mixer, 0, ByVal 0)
    Call BASS_ChannelSetAttribute(StrChan, BASS_ATTRIB_BUFFER, 0)
So I send the record channel to a mixer, and I splitted the mixer output to 3 streams (one for VU-Meters, one for Recordinn to WAV, one for Streaming to Icecast).
I start the stream for VU-Meters directly, VU-Meters displays the input level in realtime.

With 2 buttons (one Start, one Stop, for the moment, I will combinate both for one button after):

Private Sub btRecord_Click()
Call BASS_Encode_Start(RecChan, "output.wav", BASS_ENCODE_PCM Or BASS_ENCODE_AUTOFREE, 0, 0)
Call BASS_ChannelStart(RecChan)
End Sub

Private Sub btStop_Click()
Call BASS_ChannelStop(RecChan)
Call BASS_Encode_Stop(RecChan)
End Sub
But there is some delay somewhere, resulting to having on the recording, the sound before starting record, around 2 sec, and the end is missing.

To image this, I recorded 4 beeps. I click on btRecord and directly made 4 beeps, and at the end of the 4th beep, I clicked on btStop button to stop the recording.

Here is the result:recording delayed.jpg

How can I record only since I click on btRecord, and stop recording without truncating the end?

Thanks  ;D

Ian @ un4seen

I'm not sure you really need splitters for that. It seems like you could just set the encoders directly on the recording? Something like this:

// start recording (with built-in simple RECORDPROC)
recording = BASS_RecordStart(44100, 2, 0, RECORDPROC_TRUE, NULL);

// start WAV writing
wavwriter = BASS_Encode_StartPCMFile(recording, 0, "output.wav");

// stop WAV writing
BASS_Encode_Stop(wavwriter);

// start casting
caster = BASS_Encode_MP3_Start(recording, "", 0, NULL, NULL);
BASS_Encode_CastInit(caster, ...);

// stop casting
BASS_Encode_Stop(caster);

Couin

Hi Ian,

Thanks for tip :)
I tried but BASS_Encode_StartPCMFile is missing in bassenc.bas (VB).
I noticed some other things are missing too, like ENCODEPROCEX, BASS_ENCODE_STOP_ASYNC, BASS_ENCODE_STOP_WAIT , BASS_ENCODE_STOP_TERMINATE and perhaps others.
I added, in bassenc.bas:Declare Function BASS_Encode_StartPCM Lib "bassenc.dll" (ByVal chan As Long, ByVal form As Any, ByVal flags As Long, ByVal proc As Long, ByVal user As Long) As Long
Declare Function BASS_Encode_StartPCMFile Lib "bassenc.dll" (ByVal chan As Long, ByVal flags As Long, ByVal filename As String) As Long
and:Function ENCODERPROC(ByVal handle As Long, ByVal channel As Long, ByVal buffer As Long, ByVal length As Long, ByVal maxout As Long, ByVal user As Long) As Long

    ' CALLBACK FUNCTION !!!

    ' Encoder callback function.
    ' handle: The(encoder)
    ' channel: The channel handle
    ' buffer : Buffer containing the PCM data (input) and receiving the encoded data (output)
    ' length : Number of bytes in (-1=closing)
    ' maxout : Maximum number of bytes out
    ' user: The() 'user' parameter value given when calling BASS_Encode_StartUser
    ' RETURN : The amount of encoded data (-1=stop)

End Function
and it looks OK, and resolving the recording delay and truncate :)

Ian @ un4seen

Quote from: CouinI tried but BASS_Encode_StartPCMFile is missing in bassenc.bas (VB).
I noticed some other things are missing too, like ENCODEPROCEX, BASS_ENCODE_STOP_ASYNC, BASS_ENCODE_STOP_WAIT , BASS_ENCODE_STOP_TERMINATE and perhaps others.

Oops! Those things were added in the recent BASSenc 2.4.17 release, but I see the updated BASSENC.BAS file wasn't included. I've added it now, so please re-download to get it.

Couin

Yeah ! Thanks, it's good !

Merry Christmas :)

Couin

Hi Ian,

Thanks for answer.

Here is some parts of my code. This is for the same software where I get the input level, record the input as wav file, and also stream to an icecast server
At software launch:
If (BASS_RecordInit(DeviceIn) = 0) Then
    Call MsgBoxEx("Can't initialize device", vbCritical)
    End
End If
recording = BASS_RecordStart(44100, 2, 0, RECORDPROC_TRUE, 0)

On clicking on the Start Recording button, the code implented from your example of message #1:
WavWriter = BASS_Encode_StartPCMFile(recording, 0, RecFile)
MultiPart = 1
NextSplit = 60 * SplitInterval * MultiPart + BASS_ChannelBytes2Seconds(recording, BASS_ChannelGetPosition(recording, BASS_POS_BYTE))
SplitSyncByte = BASS_ChannelSeconds2Bytes(recording, NextSplit) 'byte position to sync/split at
SplitSetSync = BASS_ChannelSetSync(recording, BASS_SYNC_POS, SplitSyncByte, AddressOf SplitSyncProc, 0)

The SplitSyncProc code:
Public Sub SplitSyncProc(ByVal handle As Long, ByVal channel As Long, ByVal data As Long, ByVal user As Long)
Call RecFileMulti
Call BASS_Encode_Stop(WavWriter)
WavWriter = BASS_Encode_StartPCMFile(recording, 0, RecFile)
NextSplit = (60 * SplitInterval) + NextSplit
SplitSyncByte = BASS_ChannelSeconds2Bytes(recording, NextSplit) 'byte position to sync/split at
SplitSetSync = BASS_ChannelSetSync(recording, BASS_SYNC_POS, SplitSyncByte, AddressOf SplitSyncProc, 0)
End Sub

Where MultiPart is used to increment value of MultiPart (and name next recording part file) by RecFileMulti calling.
Each SplitSyncProc call, creates the next ChannelSetSync
SplitInterval is the amount of minutes between each splits.

With music tests, I can not ear any glitch or missing/doubled part of audio, this is a good thing.
To be sure, I recorded a sine, and joining them looks perfect:
joined parts.jpg
(part1 in blue, part2 in black)

If think I should keep this code, unless you find something wrong :)

Ian @ un4seen

Looks good :)

One little thing you could do is add the BASS_SYNC_ONETIME flag to the BASS_ChannelSetSync calls, to remove the syncs after they're called:

SplitSetSync = BASS_ChannelSetSync(recording, BASS_SYNC_POS Or BASS_SYNC_ONETIME, SplitSyncByte, AddressOf SplitSyncProc, 0)

Couin

Hi Ian,

I finally made with another method. I no longer use sync. As well as I use a timer to measure recording time and file size, I measure the size of actual part of recording and once slipt size reached, I call a function where I stop and start the encoder :)