Sign in to follow this  

Music\Sounds over Network (SDL_Net)

This topic is 2055 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Alright, my library of choice is SDL_Net, documentation isn't that great, but I bet it's pretty simple once I learn how transfer things between the client. I've got a simple console application using the little tutorial on the SDL_Net Wiki. What it does is create a TCP connection between the client and server and the client can send messages to the server, and the server prints them out on the server console. The tutorial came with two commands that it does a strcmp with this char buffer[512] which are exit and quit. Obvious as they are, "exit" closes the client; "quit" closes both the server and the client.

So I added in my own commands and allowed the server to play music\sounds, but what good is that if I'm away from the server? I also followed another guide to get the IP to appear in the x.x.x.x format as opposed to the hex format which was made for winsock, but still works all the same.

Anyway, we come to the problem that I'm trying to figure out is how can I play music\sounds from the server and play them on the client? (Streaming) I assume I need a buffer or some sort to send a little bit at a time, and free the memory once it's done, but to be perfectly honest, I have no idea how.

Anyhow, here's my current server code (Since I'm new to this, I left the comments from the SDL_Net Wiki page there):


[CODE]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL.h>
#include <SDL_mixer.h>
#include <SDL_net.h>
Mix_Music* alien = NULL;
TCPsocket sd, csd; /* Socket descriptor, Client socket descriptor */
IPaddress ip, *remoteIP;
void Clean()
{
SDLNet_TCP_Close(sd);
SDLNet_Quit();
if (Mix_PlayingMusic() == 1)
Mix_HaltMusic();
Mix_FreeMusic(alien);
Mix_CloseAudio();
}
int main(int argc, char **argv)
{
if( Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 4096) < 0)
{
FILE* lerr = fopen("errlog.txt", "wb+");
fprintf(lerr, "Error opening audio.\n");
fclose(lerr);
exit(EXIT_FAILURE);
}
atexit(Clean);
alien = Mix_LoadMUS("alien.mp3");
if (!alien)
{
FILE* lerr = fopen("errlog.txt", "wb+");
fprintf(lerr, "Error opening alien.mp3.\n");
fclose(lerr);
exit(EXIT_FAILURE);
}
int quit, quit2;
char buffer[512];
if (SDLNet_Init() < 0)
{
FILE* lerr = fopen("errlog.txt", "wb+");
fprintf(lerr, "SDLNet_Init: %s\n", SDLNet_GetError());
fclose(lerr);
exit(EXIT_FAILURE);
}
/* Resolving the host using NULL make network interface to listen */
if (SDLNet_ResolveHost(&ip, NULL, 2000) < 0)
{
FILE* lerr = fopen("errlog.txt", "wb+");
fprintf(lerr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError());
fclose(lerr);
exit(EXIT_FAILURE);
}
/* Open a connection with the IP provided (listen on the host's port) */
if (!(sd = SDLNet_TCP_Open(&ip)))
{
FILE* lerr = fopen("errlog.txt", "wb+");
fprintf(lerr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
fclose(lerr);
exit(EXIT_FAILURE);
}
/* Wait for a connection, send data and term */
quit = 0;
while (!quit)
{
/* This check the sd if there is a pending connection.
* If there is one, accept that, and open a new socket for communicating */
if ((csd = SDLNet_TCP_Accept(sd)))
{
/* Now we can communicate with the client using csd socket
* sd will remain opened waiting other connections */
/* Get the remote address */
if ((remoteIP = SDLNet_TCP_GetPeerAddress(csd)))
{
/* Print the address, converting in the host format */
unsigned char IP[4] = {0,0,0,0};
for (int i=0; i<4; i++)
{
IP[i] = ( SDLNet_Read32(&remoteIP->host) >> (i*8) ) & 0xFF;
}
printf("Client connected: %d.%d.%d.%d:%d\n", IP[3],IP[2],IP[1],IP[0], SDLNet_Read16(&remoteIP->port));
}
else
{
FILE* lerr = fopen("errlog.txt", "wb+");
fprintf(lerr, "SDLNet_TCP_GetPeerAddress: %s\n", SDLNet_GetError());
fclose(lerr);
}
quit2 = 0;
while (!quit2)
{
if (SDLNet_TCP_Recv(csd, buffer, 512) > 0)
{
printf("Client said: %s\n", buffer);
if(strcmp(buffer, "exit") == 0) /* Terminate this connection */
{
quit2 = 1;
printf("Client terminated the connection...\n");
}
if(strcmp(buffer, "quit") == 0) /* Quit the program */
{
quit2 = 1;
quit = 1;
printf("Client terminated server...\n");
}
if (strcmp(buffer, "alien") == 0)
{
if (Mix_PlayingMusic() == 0)
Mix_PlayMusic(alien, -1);
}
if (strcmp(buffer, "stop") == 0)
{
if (Mix_PlayingMusic() == 1)
Mix_HaltMusic();
}
if (strcmp(buffer, "pause") == 0)
{
if (Mix_PlayingMusic() == 1)
Mix_PauseMusic();
}
if (strcmp(buffer, "resume") == 0)
{
if (Mix_PausedMusic() == 1)
Mix_ResumeMusic();
}
}
}
/* Close the client socket */
SDLNet_TCP_Close(csd);
}
}
return EXIT_SUCCESS;
}
[/CODE]

And here is my client code:


[CODE]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL_net.h>
int main(int argc, char **argv)
{
IPaddress ip; /* Server address */
TCPsocket sd; /* Socket descriptor */
int quit, len;
char buffer[512];
/* Simple parameter checking */
if (argc < 3)
{
FILE* lerr = fopen("lerr.txt", "w+");
fprintf(lerr, "Usage: %s host port\n", argv[0]);
fclose(lerr);
exit(EXIT_FAILURE);
}
if (SDLNet_Init() < 0)
{
FILE* lerr = fopen("lerr.txt", "w+");
fprintf(lerr, "SDLNet_Init: %s\n", SDLNet_GetError());
fclose(lerr);
exit(EXIT_FAILURE);
}
/* Resolve the host we are connecting to */
if (SDLNet_ResolveHost(&ip, argv[1], atoi(argv[2])) < 0)
{
FILE* lerr = fopen("lerr.txt", "w+");
fprintf(lerr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError());
fclose(lerr);
exit(EXIT_FAILURE);
}
/* Open a connection with the IP provided (listen on the host's port) */
if (!(sd = SDLNet_TCP_Open(&ip)))
{
FILE* lerr = fopen("lerr.txt", "w+");
fprintf(lerr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
fclose(lerr);
exit(EXIT_FAILURE);
}
/* Send messages */
quit = 0;
while (!quit)
{
printf(">");
scanf("%s", buffer);
len = strlen(buffer) + 1;
if (SDLNet_TCP_Send(sd, (void *)buffer, len) < len)
{
FILE* lerr = fopen("lerr.txt", "w+");
fprintf(lerr, "SDLNet_TCP_Send: %s\n", SDLNet_GetError());
fclose(lerr);
exit(EXIT_FAILURE);
}
if(strcmp(buffer, "exit") == 0)
quit = 1;
if(strcmp(buffer, "quit") == 0)
quit = 1;
}
SDLNet_TCP_Close(sd);
SDLNet_Quit();
return EXIT_SUCCESS;
}
[/CODE]
Any help is appreciated

Share this post


Link to post
Share on other sites
Is there a particular reason you want to stream the sound to the client? Typically you would just send a command from the server to the client that says "play sound #3845" or whatever, and the client uses a local sound resource to actually fulfill the command.

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1335402078' post='4934936']
Is there a particular reason you want to stream the sound to the client? Typically you would just send a command from the server to the client that says "play sound #3845" or whatever, and the client uses a local sound resource to actually fulfill the command.
[/quote]
Well, I understand that, that's simple to do, but I want to understand for one, how streaming works, and two, how to copy files over a network. If I can do those and I understand how, I feel pretty much golden. Except the fact that I made local file pointers in the server and opened them with "wb+" if there was an error, aye, what a lamebrain mistake.

Share this post


Link to post
Share on other sites
Alright after working at something related to printing to the client's console, I realized something. It can't be done with SDLNet_TCP_Send, as it's a client only function, at least according to the documentation.
[quote]SDLNet_TCP_Send sends the data over the socket. If the data cannot be sent immediately, the routine waits until all of the data may be delivered properly (it is a blocking operation). This routine is not used for server sockets.[/quote]

The only possible way I see that I can send messages to the client through a buffer, or any data to the client, I would need some sort of double connection using multiple sockets. I tried that already, but I couldn't get it to work. Unless there's another function I'm missing.

Share this post


Link to post
Share on other sites
Answer this question: "How do I play music from a FILE on the client, when all I have is the file handle (not the path)?"
Once you have that answer, you should be very close to the answer for "how do I play music from a socket handle?"

Share this post


Link to post
Share on other sites
[quote name='hplus0603' timestamp='1335412771' post='4934959']
Answer this question: "How do I play music from a FILE on the client, when all I have is the file handle (not the path)?"
Once you have that answer, you should be very close to the answer for "how do I play music from a socket handle?"
[/quote]
Well, the way I see it is that I can have multiple Mix_Music pointers and I can do something like mus1 = Mix_LoadMUS("mus.mp3"); then have mus2 = mus1, and so on. I'm not 100% sure on that, as pointers are just now becoming a nightmare. The simple answer would be to read something into "musClient" (assuming we have the Mix_Music* musClient) that is sent over from the server that has "musServer" and loads it so the client just has to play it. A file transfer could likely be achieved the same way. The main problem with this is right now is that apparently the server in SDL_Net can't send data to the client, and can only receive data from it. The second problem is that I need the length in bytes of the Mix_Music pointer that would be sent, which as I understand would only return 4 with any given pointer.

Aye, I'm only 16 maybe I should just give up until I have an actual teacher.

Share this post


Link to post
Share on other sites
[quote]Mix_LoadMUS("mus.mp3");[/quote]

That's a filename, not a file pointer.

It *may* be that the library you are using doesn't actually support playing from a socket handle. If so, see if it supports playing from raw memory, and if so, download the file to memory and play from there.


[quote]Aye, I'm only 16 maybe I should just give up until I have an actual teacher. [/quote]

Age is just a number! Don't give up when it gets hard. Instead, I find I make progress if I can step back and figure out *why* something is hard, and then figure out how to remove that underlying problem. Books, tutorials, tutors, forums, reading other people's code, and sleeping on the problem are all important parts of learning, and no one solution will always be right.
To become a programmer, you need a really high tolerance level for frustration, though :-)

Share this post


Link to post
Share on other sites
[quote name='hplus0603' timestamp='1335550109' post='4935440']
[quote]Mix_LoadMUS("mus.mp3");[/quote]

That's a filename, not a file pointer.

It *may* be that the library you are using doesn't actually support playing from a socket handle. If so, see if it supports playing from raw memory, and if so, download the file to memory and play from there.


[quote]Aye, I'm only 16 maybe I should just give up until I have an actual teacher. [/quote]

Age is just a number! Don't give up when it gets hard. Instead, I find I make progress if I can step back and figure out *why* something is hard, and then figure out how to remove that underlying problem. Books, tutorials, tutors, forums, reading other people's code, and sleeping on the problem are all important parts of learning, and no one solution will always be right.
To become a programmer, you need a really high tolerance level for frustration, though :-)
[/quote]

Thanks. The file pointer I talking about is when you create a Mix_Music*. This can be found under the SDL_Mixer library. There's a reason I made a thread before this asking about which libraries to use for networking and SDL_Net was appealing for it's portability and its ability to be able to use plain C or C++.

Either way, I'm not trying to recreate the same topic. I've been thinking a bit backwards here, I'm trying to figure out how to send music from a server to a client without first trying to figure out how to so much as send information to the client. The server can receive information fine, and the client can send information fine, but not the other way around. The server cannot send information, and the client can't receive information. I tried the simple creating a second char to send to the client. It would actually be more appropriate to send an int for this particular situation. Either way I tried sending a char to the client containing something like '0' or '1' and then have the client print out a message corresponding to the number it got.

This is impossible as I said before, the SDLNet_TCP_Send() is designed for the client only (according to the documentation). So then, how can I possibly send data to the client? The odd part is I'm using TCP, and looking at the documentation, there doesn't appear to be such a limitation to UDP.

Share this post


Link to post
Share on other sites
[quote]the SDLNet_TCP_Send() is designed for the client only (according to the documentation).[/quote]

[url="http://sdl.beuc.net/sdl.wiki/SDLNet_TCP_Send"]It is?[/url]

Perhaps this is confusing:[quote]... such as the client disconnecting.[/quote]
I prefer the term "peer" for this purpose, since it doesn't confuse terminology or imply direction.

But no, client here means "the thing on the other side of TCP connection", nothing more.

[quote]The server cannot send information, and the client can't receive information.[/quote]

If you can send in one direction, you can send in other. But hard to say without more detail.

Have you tried first going [url="http://content.gpwiki.org/index.php/SDL:Tutorial:Using_SDL_net"]through the SDLNet tutorial[/url]? Edited by Antheus

Share this post


Link to post
Share on other sites
[quote name='Antheus' timestamp='1335626697' post='4935598']
[quote]the SDLNet_TCP_Send() is designed for the client only (according to the documentation).[/quote]

[url="http://sdl.beuc.net/sdl.wiki/SDLNet_TCP_Send"]It is?[/url]
[/quote]
Under the description read this SDLNet_TCP_Send sends the data over the socket. If the data cannot be sent immediately, the routine waits until all of the data may be delivered properly (it is a blocking operation). [b]This routine is not used for server sockets[/b]."

[quote name='Antheus' timestamp='1335626697' post='4935598']
[quote]The server cannot send information, and the client can't receive information.[/quote]

If you can send in one direction, you can send in other. But hard to say without more detail.

Have you tried first going [url="http://content.gpwiki.org/index.php/SDL:Tutorial:Using_SDL_net"]through the SDLNet tutorial[/url]?
[/quote]
Everything I have is from the SDL_Net tutorial and messing with the code.

[quote name='Spirrwell' timestamp='1335401164' post='4934929']
Anyhow, here's my current server code [b](Since I'm new to this, I left the comments from the SDL_Net Wiki page there)[/b]:
[/quote]

After all that is kind of why I'm here, because there's that one tutorial, and barely any others if any. (As in no others that I could find) Edited by Spirrwell

Share this post


Link to post
Share on other sites
Also, the fact that some networking APIs use "char* and size_t" for "data and length" may be confusing -- what it really means is just "a pointer to memory of a certain amount of bytes" and would be better expressed with "void*." Twenty years ago, not all compilers were as good with void*, so char* was the "poor man's alternative."

Share this post


Link to post
Share on other sites
[quote]Under the description read this SDLNet_TCP_Send sends the data over the socket. If the data cannot be sent immediately, the routine waits until all of the data may be delivered properly (it is a blocking operation). [b]This routine is not used for server sockets[/b]."[/quote]

Weird.

I can understand the reasoning behind it. TCP does have inherent blocking behavior built-in, but enforcing it completely at API is unusual. Even Java supports non-blocking behavior.

I suppose SDLNet really does want people to use UDP.

[quote]After all that is kind of why I'm here, because there's that one tutorial, and barely any others if any. (As in no others that I could find)[/quote]

Is there a reason why this must be in C?

And looking through examples, SDLNet doesn't really strike me as very valuable to learn, considering it offers no meaningful abstractions and some unacceptable restrictions.

One could use threads, but it's not a good solution. SDL is aimed at primarily single-threaded operation, with plethora of global state. IIRC, some parts even cannot be used in multi-threaded environment. And if Net restrictions are in place for portability reasons, well, non-blocking sockets are much more useful and likely to be supported than threads.

I've only skimmed over the thing, but I don't grok the design choices and don't consider potential workarounds acceptable.

Share this post


Link to post
Share on other sites
[quote name='Antheus' timestamp='1335647157' post='4935680']
[quote]Under the description read this SDLNet_TCP_Send sends the data over the socket. If the data cannot be sent immediately, the routine waits until all of the data may be delivered properly (it is a blocking operation). [b]This routine is not used for server sockets[/b]."[/quote]

Weird.

I can understand the reasoning behind it. TCP does have inherent blocking behavior built-in, but enforcing it completely at API is unusual. Even Java supports non-blocking behavior.

I suppose SDLNet really does want people to use UDP.

[quote]After all that is kind of why I'm here, because there's that one tutorial, and barely any others if any. (As in no others that I could find)[/quote]

Is there a reason why this must be in C?

And looking through examples, SDLNet doesn't really strike me as very valuable to learn, considering it offers no meaningful abstractions and some unacceptable restrictions.

One could use threads, but it's not a good solution. SDL is aimed at primarily single-threaded operation, with plethora of global state. IIRC, some parts even cannot be used in multi-threaded environment. And if Net restrictions are in place for portability reasons, well, non-blocking sockets are much more useful and likely to be supported than threads.

I've only skimmed over the thing, but I don't grok the design choices and don't consider potential workarounds acceptable.
[/quote]

Alright, it's settled, SDL_net isn't that great of a library. I explained why I wanted C in the other thread I created, as I work with obscure operating systems. I'm learning Assembly and such and C is better for that portability if the library doesn't support an OS, as C itself doesn't have OS dependent functions. Now that out of the way, my alternative library was enet. It's very small, and it's C. Also it uses only UDP, but they added the ability to mark a packet with a flag to be a reliable packet, just like TCP. It's also built without a DLL if you use Windows, although it can if you build it with some DLL flag.

ENet is perfect for me, so I'll use it instead. To get back to my original problem of trying to play music/sounds over the network. I just need to know how to load a file (in this case a music file) into a byte array with a way to get the length in bytes, send it to the client (I can do this part), and recreate the file on the client (in this case, temporarily).

So, as always, any help is appreciated.

Share this post


Link to post
Share on other sites
Loading a file into memory:

[code]
FILE *file = fopen("justin_biebers_greatest_hits_megamix.mp3", "rb");
if (!file) { return FAILURE; }
fseek(file, 0, 2);
size_t len = ftell(file); // could use fpos_t on most OS-es
fseek(file, 0, 0);
void *buf = malloc(len);
if (!buf) { fclose(file); return FAILURE; }
fread(buf, 1, len, file);
fclose(file);
// OK, now "buf" contains the file data, and "l" contains the number of bytes
[/code]

Share this post


Link to post
Share on other sites
[quote name='hplus0603' timestamp='1335658083' post='4935721']
Loading a file into memory:

[code]
FILE *file = fopen("justin_biebers_greatest_hits_megamix.mp3", "rb");
if (!file) { return FAILURE; }
fseek(file, 0, 2);
size_t len = ftell(file); // could use fpos_t on most OS-es
fseek(file, 0, 0);
void *buf = malloc(len);
if (!buf) { fclose(file); return FAILURE; }
fread(buf, 1, len, file);
fclose(file);
// OK, now "buf" contains the file data, and "l" contains the number of bytes
[/code]
[/quote]
Yay, my first time using malloc(). Having a bit of trouble getting the client to right the packet data correctly, but it's farther than I've gotten, thank you!

Share this post


Link to post
Share on other sites
[quote name='Antheus' timestamp='1335647157' post='4935680']
I can understand the reasoning behind it. TCP does have inherent blocking behavior built-in, but enforcing it completely at API is unusual. Even Java supports non-blocking behavior.
[/quote]

I would not say that a blocking operation on send is any trouble, I mean how often can data not be sent?

I would recomemnd the OP to lookup socket sets, it is the only way I found it possible to have non-blocking recieves in SDL_Net:

[CODE]
TCPsocket mSock;
SDLNet_SocketSet mSet;

<some code here>

int numReady;
short header;
int received;

numReady = SDLNet_CheckSockets(mSet, 0);

if(numReady == 0)
return 0;

received = SDLNet_TCP_Recv(mSock, &header, 2);
[/CODE]

Share this post


Link to post
Share on other sites
[quote name='flodihn' timestamp='1335732754' post='4935892']
I would not say that a blocking operation on send is any trouble, I mean how often can data not be sent?[/quote]

If socket has too many unacked frames, it will block. In addition to usual kernel buffer overflow, where just the send buffer fills up.

However:[code]send(socket, buffer, 1024 * 1024);[/code]and you're pretty much stuck for at least one round trip. While not necessarily a big deal on its own, this is the inner server loop, so for the 75ms, your server is waiting for ack, idling. 14 clients, and you're capped.

It's less of a problem on LAN, but the problem doesn't go away.

Slow start is less of a problem for persistent connections, but add some packet loss and always-blocking sockets simply aren't an option.

[quote]I found it possible to have non-blocking recieves in SDL_Net:[/quote]

Receives are fine, it's sending that's the problem. Edited by Antheus

Share this post


Link to post
Share on other sites
Typically, you will want to keep a queue in the application for outgoing data, and if that queue grows too big, you deem the remote peer unable to keep up, and drop/disconnect it.
This queue could be kept in the kernel, in the form of the socket buffer, and you can use non-blocking sends, and when the non-blocking send fails, you deem the queue full, and disconnect the client. Edited by hplus0603

Share this post


Link to post
Share on other sites

This topic is 2055 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this