24 May '13 - 09:24 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: Silent stream (shoutcast) recording  (Read 1434 times)
JacekH
Posts: 24


« on: 7 Aug '10 - 01:00 »
Reply with quoteQuote

I initialize BASS with a default device (as BASS_Init(-1, 44100, 0,...), I create a stream as BASS_StreamCreateURL(PAnsiChar(AUrl), 0, BASS_STREAM_STATUS, @StatusProc... In StatusProc I write received buffer to a disk. And it works perfectly. Because I need to record multiple streams into multiple files I need 'silent recording' and I initialize BASS with no sound (BASS_Init(0, 44100, 0,...) BASS_ChannelPlay fails and I get an error 37 (BASS_ERROR_NOTAVAIL ) and info "No data, file removed". If I create a stream for decoding as BASS_StreamCreateURL(PAnsiChar(AUrl), 0, BASS_STREAM_STATUS or BASS_STREAM_DECODE, @StatusProc... I get an error 38 (BASS_ERROR_DECODE).

How should I properly initialize and open the stream for recording with no sound?

Regards,
Jacek
Logged
Ian @ un4seen
Administrator
Posts: 15270


« Reply #1 on: 9 Aug '10 - 14:51 »
Reply with quoteQuote

You should create decoding channels (ie. use the BASS_STREAM_DECODE flag) and then repeatedly call BASS_ChannelGetData to process them. It could look something like this...

stream=BASS_StreamCreateURL(url, 0, BASS_STREAM_STATUS|BASS_STREAM_DECODE, StatusProc, NULL); // create a stream (decoding channel) from the URL
while (BASS_ChannelIsActive(stream)) { // not ended yet
BYTE buf[20000];
BASS_ChannelGetData(stream, buf, sizeof(buf)); // process some data from the stream
Sleep(50); // wait a bit
}

If you have multiple streams, you could add extra BASS_ChannelGetData calls to the loop for each of them (and update the "while" loop condition), or you could have multiple loops running in separate threads.
Logged
JacekH
Posts: 24


« Reply #2 on: 9 Aug '10 - 14:57 »
Reply with quoteQuote

BASS initialization:
BASS_Init(0, 44100, 0, StreamManager.FHandle, nil)
BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 1); // enable playlist processing
BASS_SetConfig(BASS_CONFIG_NET_PREBUF, 0);

I create AAC stream:

FChan := BASS_AAC_StreamCreateURL(PAnsiChar(AUrl), 0, BASS_STREAM_STATUS, @StatusProc, Pointer(Self));

where StatusProc

procedure StatusProc(Buffer: Pointer; Len, User: DWORD); stdcall;
var
  TTS: TThreadedStream;
begin
  TTS := TThreadedStream(User);
  if (TTS <> nil) and (Buffer <> nil) then
    TTS.DoStatus(Buffer, Len);
end;

procedure TThreadedStream.DoStatus(Buffer: Pointer; Len: DWORD);
begin
  if (Len > 0) then
  begin
    if Assigned(FFileStream) then
      FFileStream.Write(Buffer^, Len);
  end else
    DoLog(String(PAnsiChar(Buffer)), TS_STATUS);
end;

I use code from BASS Radio demo:

Progress := 0;
    repeat
      Len := BASS_StreamGetFilePosition(FChan, BASS_FILEPOS_END);
      if (Len = DW_Error) then
      begin
        Result := False;
        DoLog('Error code: ' + IntToStr(Bass_ErrorGetCode()), TS_STATUS);
        Break;
      end;
      Progress := (BASS_StreamGetFilePosition(FChan, BASS_FILEPOS_DOWNLOAD) -
            BASS_StreamGetFilePosition(FChan, BASS_FILEPOS_CURRENT)) * 100 div Len;
    until (Progress > BUFFER_PRE_CACHE) or (not Result);
    if not Result then Exit;
    icy := BASS_ChannelGetTags(FChan, BASS_TAG_ICY);
    if (icy = nil) then
      icy := BASS_ChannelGetTags(FChan, BASS_TAG_HTTP); // no ICY tags, try HTTP
    if (icy <> nil) then
    begin
      while (icy^ <> #0) do
      begin
        DoLog(String(icy), TS_TAGS);
        icy := icy + Length(icy) + 1;
      end;
      DoMeta;
    end;
    BASS_ChannelSetSync(FChan, BASS_SYNC_META, 0, @MetaSync,  Pointer(Self));
    DoLog('Stream opened', TS_STATUS);

to finally play the stream (Execute method of the thread):

if BASS_ChannelPlay(FChan, True) then
begin
  while (not Terminated) and (Now < FRecEnd) do Sleep(5);
BASS_ChannelStop(FChan);
DoLog('Recording stopped', TS_STATUS);
end else
  DoLog('Recording not started. Error code: ' + IntToStr(Bass_ErrorGetCode()), TS_STATUS);

and it doesn't play - I get an error 38.

Regards,
Jacek

P.S. Thank you for the answer, I was almost ready to post my answer when you did it first Wink
Logged
JacekH
Posts: 24


« Reply #3 on: 9 Aug '10 - 17:44 »
Reply with quoteQuote

Dear Ian,

I did as you suggested but now a recorded stream is not recognized as a valid audio file. I extracted the most important code:

uses bass, bass_aac;

var
    FChan: HSTREAM;

procedure StatusProc(Buffer: Pointer; Len, User: DWORD); stdcall;
begin
  if (Len = 0) and (Buffer <> nil) then
    Form1.mmo1.Lines.Append(String(PAnsiChar(Buffer)));
end;

procedure MetaSync(handle: HSYNC; channel, data, user: DWORD); stdcall;
var
  Meta: PAnsiChar;
  I: Integer;
begin
  Meta := BASS_ChannelGetTags(FChan, BASS_TAG_META);
  if (Meta <> nil) then
  begin
    I := Pos('StreamTitle=', String(AnsiString(Meta)));
    if I > 0 then
    begin
      I := I + 13;
      Form1.mmo1.Lines.Append(AnsiString(Copy(Meta, I, Pos(';', String(Meta)) - I - 1)));
    end;
  end;
end;

procedure TForm1.btnRecordClick(Sender: TObject);
const
  BUFFER_SIZE = 20480;
var
  FBuffer: Pointer;
  FBuffRecorded: DWORD;
  FFileStream: TFileStream;
begin
  FStopped := False;
  FFileStream := TFileStream.Create('test.mp3', fmCreate);
  GetMem(FBuffer, BUFFER_SIZE);
  try
    FChan := BASS_AAC_StreamCreateURL (PAnsiChar('http://gra.radio.kielce.com.pl:8000/rk1'), 0, BASS_STREAM_STATUS or BASS_STREAM_DECODE, @StatusProc, nil);
    if FChan <> 0 then
    begin
      BASS_ChannelSetSync(FChan, BASS_SYNC_META, 0, @MetaSync,  Pointer(Self));
      while (not FStopped) and (BASS_ChannelIsActive(FChan) <> BASS_ACTIVE_STOPPED) do
      begin
        FBuffRecorded := BASS_ChannelGetData(FChan, FBuffer, BUFFER_SIZE);
        FFileStream.Write(FBuffer^, FBuffRecorded);
        Application.ProcessMessages;
        Sleep(50);
      end;
    end;
  finally
    FreeMem(FBuffer);
    FFileStream.Free;
  end;
end;

What's wrong with it?

Kindest regards,
Jacek
Logged
Ian @ un4seen
Administrator
Posts: 15270


« Reply #4 on: 10 Aug '10 - 13:50 »
Reply with quoteQuote

That code would save the decoded PCM sample data. Is that what you wanted, or did you want to save the encoded MP3 stream data? If the latter, you should write the data received in the DOWNLOADPROC ("StatusProc") instead of what's returned by the BASS_ChannelGetData call. If you did want to save the PCM data, you could encapsulate it in a WAVE file to make it playable; using the BASSenc add-on (eg. BASS_Encode_Start + BASS_ENCODE_PCM) would be the simplest way to do that.
Logged
JacekH
Posts: 24


« Reply #5 on: 10 Aug '10 - 14:23 »
Reply with quoteQuote

Quote
That code would save the decoded PCM sample data. Is that what you wanted, or did you want to save the encoded MP3 stream data? If the latter, you should write the data received in the DOWNLOADPROC ("StatusProc") instead of what's returned by the BASS_ChannelGetData call.

I need to save in the original format (probably mp3, aac). I saved the data in StatusProc but it worked when I initialized BASS with the default audio device. As I mentioned above it didn't worked with NO SOUND initialization giving me an error. Then I changed the way of opening the stream to DECODE and I got another error. You suggested reading the data by calling BASS_ChannelGetData [and if I understand correctly - without getting audio in StatusProc], and I was sure I got the same format of stream data, not decoded to PCM format.

So my question is: is it possible to open and read stream with NO SOUND and save its content in its original format to a file?

Regards,
Jacek
Logged
Ian @ un4seen
Administrator
Posts: 15270


« Reply #6 on: 10 Aug '10 - 16:28 »
Reply with quoteQuote

So my question is: is it possible to open and read stream with NO SOUND and save its content in its original format to a file?

Yes, that should be possible without any modification of the DOWNLOADPROC ("StatusProc") code. You just need to replace the BASS_ChannelPlay call with a BASS_ChannelGetData loop, eg. as in my 1st reply. You can ignore the sample data returned by BASS_ChannelGetData, it's only being used to process/advance the stream in this case.
Logged
JacekH
Posts: 24


« Reply #7 on: 10 Aug '10 - 22:00 »
Reply with quoteQuote

Ian, thank you for your patient and great support Smiley Now it works as expected.

Kindest regards,
Jacek
Logged
barpas
Posts: 5


« Reply #8 on: 13 Apr '11 - 02:30 »
Reply with quoteQuote

it isn't good metod - for few reasons, eg i'would like to have posibility turn on/off sound in one of srreams, besides in that metod i have problems to interpreting metatags

is there ohter metod to turn off sound while recording stream ?
Logged
Ian @ un4seen
Administrator
Posts: 15270


« Reply #9 on: 13 Apr '11 - 15:12 »
Reply with quoteQuote

If you want to be able to listen to the stream sometimes, instead of using a "decoding channel" (BASS_STREAM_DECODE), you could create a normal playback stream and mute it via BASS_ChannelSetAttribute (BASS_ATTRIB_VOL) when you don't want to hear it.
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines